Weather Effects¶
Problem¶
You want room descriptions to change dynamically based on the current weather — rain makes streets slippery, fog hides exits, snow blankets the ground.
Solution¶
MAID's ExtendedRoomComponent has built-in support for weather-variant descriptions. No custom system is needed for the basic case.
Setting Up Weather Descriptions (Builder Commands)¶
@room weather here clear The marketplace bustles under a bright sky.
@room weather here rain Rain hammers the cobblestones. Merchants huddle under awnings.
@room weather here fog Thick fog rolls through the square. You can barely see your hand.
@room weather here snow A blanket of fresh snow covers the marketplace. Footprints criss-cross the ground.
@room mood here bustling
@room atmosphere here The smell of baking bread drifts from a nearby stall.
Programmatic Setup¶
from uuid import UUID
from maid_engine.core.world import World
from maid_stdlib.components import DescriptionComponent
from maid_stdlib.components.extended_room import (
ExtendedRoomComponent,
ExtendedDescriptions,
Weather,
TimeOfDay,
Season,
RoomDetail,
)
def create_weather_room(world: World) -> UUID:
"""Create a room with weather-reactive descriptions."""
room = world.create_entity()
room.add(DescriptionComponent(
name="Town Square",
keywords=["square", "town"],
))
room.add(ExtendedRoomComponent(
descriptions=ExtendedDescriptions(
base_description=(
"A wide cobblestone square surrounded by timber-framed shops."
),
# Weather-specific overlays
weather_effects={
Weather.CLEAR: (
"Sunlight warms the stones. Street performers "
"entertain a small crowd."
),
Weather.RAIN: (
"Rain hammers the cobblestones, pooling in the "
"gutters. Most shops have closed their shutters."
),
Weather.FOG: (
"Dense fog swallows the square. The buildings are "
"dim shapes in the murk."
),
Weather.SNOW: (
"Fresh snow blankets everything in white. A child "
"builds a snowman near the fountain."
),
Weather.STORM: (
"Thunder cracks overhead. Lightning illuminates "
"the empty square in stark flashes."
),
},
# Time-of-day variants
time_variants={
TimeOfDay.DAWN: (
"The first light of dawn paints the rooftops gold."
),
TimeOfDay.NIGHT: (
"Lanterns cast pools of warm light across the square."
),
TimeOfDay.MIDNIGHT: (
"The square is deserted. Only a stray cat prowls "
"between the shuttered stalls."
),
},
time_mode="append", # Appended after base description
# Seasonal changes
season_variants={
Season.SPRING: "Flower boxes overflow with color.",
Season.AUTUMN: "Fallen leaves skitter across the stones.",
Season.WINTER: "Icicles hang from the eaves.",
},
season_mode="append",
# Random ambient details
random_details=[
RoomDetail(text="A merchant argues loudly with a customer."),
RoomDetail(text="A pigeon lands on the fountain rim."),
RoomDetail(text="The smell of roasted chestnuts fills the air."),
RoomDetail(
text="Snowflakes drift lazily down.",
conditions={"weather": Weather.SNOW},
),
],
max_random_details=2,
mood="lively",
atmosphere_text=(
"The constant murmur of commerce fills the air."
),
),
))
room.add_tag("room")
room.add_tag("outdoor")
world.register_room(room.id, {"name": "Market Square"})
return room.id
Rendering Descriptions¶
The ExtendedRoomComponent renders descriptions based on a context dict:
from maid_stdlib.components.extended_room import (
ExtendedRoomComponent,
Weather,
TimeOfDay,
Season,
)
def get_room_description(room_entity: Entity, weather: Weather) -> str:
"""Get the full room description for current conditions."""
ext = room_entity.try_get(ExtendedRoomComponent)
if not ext:
# Fall back to basic description
desc = room_entity.try_get(DescriptionComponent)
return desc.long_desc if desc else "You see nothing special."
context = {
"weather": weather,
"time": TimeOfDay.AFTERNOON,
"season": Season.SUMMER,
}
return ext.get_description(context)
A Simple Weather System¶
If your content pack doesn't already have weather, here's a minimal system:
import random
from maid_engine.core.ecs import System
from maid_engine.core.world import World
from maid_stdlib.components.extended_room import Weather
class SimpleWeatherSystem(System):
"""Changes weather periodically."""
priority = 10 # Run early so other systems see current weather
def __init__(self, world: World, change_interval: float = 900.0) -> None:
super().__init__(world)
self.change_interval = change_interval
self._timer: float = change_interval
self.current_weather: Weather = Weather.CLEAR
async def startup(self) -> None:
self.world.set_data("weather", "current", self.current_weather)
async def update(self, delta: float) -> None:
self._timer -= delta
if self._timer <= 0:
self._timer = self.change_interval
await self._change_weather()
async def _change_weather(self) -> None:
"""Transition to a new weather state."""
candidates = [
w for w in Weather
if self.current_weather.can_transition_to(w)
]
if candidates:
self.current_weather = random.choice(candidates)
self.world.set_data("weather", "current", self.current_weather)
How It Works¶
- ExtendedRoomComponent stores weather/time/season description variants in
ExtendedDescriptions get_description(context)merges base + weather + time + season + random detailstime_mode/season_modecontrols whether variants replace or append to the base descriptionWeather.can_transition_to()ensures realistic weather progression (e.g., CLEAR → STORM is blocked; CLEAR → CLOUDY → RAIN is valid)RoomDetail.conditionsallows details that only appear under certain conditionsworld.set_data("weather", "current", ...)stores global state that any system can read
Variations¶
- Indoor rooms: Skip weather entirely — don't add
weather_effectsto indoor rooms - Movement penalties: Check
Weather.movement_modifierto slow travel in storms - Visibility: Use
Weather.visibility_modifierto hide entities or shorten look range in fog - Regional weather: Store weather per-area instead of globally using area-namespaced data
- Weather events: Emit a
WeatherChangedEventso other systems can react (NPCs seek shelter, etc.)
See Also¶
- Extended Rooms Guide — Full reference for dynamic room descriptions
- Timed Event — Timer-based system pattern
- ECS Components — Component design