Morwen's Weather Engine
π¦οΈ Weather Engine Introduction[edit]
The Infinite Worlds Weather Engine simulates dynamic, realistic weather for your story world. It automatically tracks temperature, precipitation, wind, humidity, and visibility based on the current date and your location's climate configuration. Each weather state (sunny, rain, freezing rain, blizzard, etc.) has customizable properties including temperature modifiers, precipitation intensity, and humidity ranges. The system transitions between states using probability matrices, ensuring natural weather progression. Daily temperature variations come from seasonal base values, random noise, and weather-specific effects. Wind direction and speed add atmospheric realism. Weather updates whenever the date advances, with each state persisting for configurable durations. Special temperature constraints ensure physical accuracy, freezing rain only occurs below its minimum temperature threshold. Fully customizable for any setting, from tropical paradises to frozen tundras.
π Weather Configuration Guide[edit]
This guide explains how to create your own weather configuration for the Infinite Worlds weather simulation system.
π― Overview[edit]
The weather config is a YAML-based tracked item that defines: Temperature patterns for your location Available weather states and their properties How weather transitions between states Wind, humidity, and other atmospheric conditions The system automatically updates weather based on date changes, ensuring realistic weather patterns for your world.
π 1. Location Settings[edit]
location: "Portland, Oregon, USA"location: Geographic location (affects temperature curves)
Purpose: Sets the context for temperature calculations.
π 2. Monthly Configuration[edit]
months:
1:
name: January
season: Winter
base_temp: 5Map of all months with the corresponding season and base_temp. base_temp is the average temperature of the month between daily and nightly temperatures.
π‘οΈ 2. Temperature Configuration[edit]
temperature:
daily_amplitude: 5.0
noise_scale: 1.5
min: -5.0
max: 20.0daily_amplitude
- Daily temperature swing (Β± from center)
- 3-10Β°C
noise_scale
- Random variation multiplier
- 0.5-3.0
min
- Absolute minimum temperature
- -20 to 0Β°C
max
- Absolute maximum temperature
- 20-50Β°C
How Temperature Works
- Final Temperature = Base + Noise + WeatherModifier Β± Amplitude/2
- Base: Month-specific temperature from base_temp
- Noise: Random variation (-noise_scale to +noise_scale)
- WeatherModifier: Temperature adjustment from current weather state
- Amplitude: Creates daily min/max range
- Example: January (base=4.0), noise=+1.2, freezing_rain modifier=-1.0, amplitude=5.0
- Center: 4.0 + 1.2 + (-1.0) = 4.2Β°C
- Range: 4.2 Β± 2.5 = 1.7Β°C to 6.7Β°C
βοΈ 3. Weather States[edit]
weather:
states:
sunny:
prob: 0.05
temp_mod: +2.0
precip: 0.0
humidity_range: [50, 65]
description: "Clear skies"
visibility: "excellent"
freezing_rain:
prob: 0.02
temp_mod: -1.0
precip: 3.0
humidity_range: [90, 100]
min_temp: -2.0
description: "Freezing rain creating icy conditions"
visibility: "poor"prob
- Base probability of this state (0-1)
- 0.01-0.5
temp_mod
- Temperature modifier for this state
- -5 to +5Β°C
precip
- Base precipitation intensity
- 0-5
humidity_range
- Min/max humidity percentage
- [50, 95]
min_temp
- Minimum required temperature (optional)
- -10 to 10Β°C
description
- Human-readable description
- "Rain"
visibility
- Visibility level
- "excellent", "normal", "reduced", "poor", "whiteout"
Special Fields min_temp: Weather cannot persist if temperature rises above this value. Forces transition to another state.
π 4. Transition Matrix[edit]
transition_matrix:
sunny:
overcast: 0.60
drizzle: 0.30
sunny: 0.10
overcast:
sunny: 0.10
drizzle: 0.35
rain: 0.25
fog: 0.10
freezing_rain: 0.05
blizzard: 0.02
ice_fog: 0.05
overcast: 0.08[state]
- Source weather state
[target_state]: probability
- Probability of transitioning to target (0-1)
How It Works
- Probabilities must sum to 1.0 for each source state
- When weather duration expires, system rolls a random number (0-1)
- Selects the first state where cumulative probability β₯ roll
- Example: From overcast:
- Roll 0.05 β sunny (0.0-0.10)
- Roll 0.25 β drizzle (0.10-0.45)
- Roll 0.75 β rain (0.45-0.70)
- Roll 0.95 β ice_fog (0.90-0.95)
β³ 5. Duration Settings[edit]
duration:
sunny: {min: 2, max: 4}
overcast: {min: 2, max: 8}
drizzle: {min: 2, max: 12}
rain: {min: 3, max: 8}min
- Minimum days this weather persists
- 1-3
max
- Maximum days this weather persists
- 3-14
How It Works
- When weather state begins, system rolls: random_between(min, max)
- Weather persists until days_in_state β₯ rolled_value
- Minimum of 2 ensures weather lasts at least 1 full day
- Example: drizzle with min=2, max=12
- Rolled duration: 5 days
- Weather changes after 5+ days of drizzle
π¨ 6. Wind Configuration[edit]
wind:
base_speed: 12.0
dominant_direction: 225
direction_variation: 45
gust_probability: 0.10
gust_multiplier: [1.5, 2.5]base_speed
- Average wind speed
- km/h or mph
dominant_direction
- Primary wind direction
- Degrees (0-360)
direction_variation
- Β± variation from dominant
- Degrees
gust_probability
- Chance of gusts (0-1)
- 0-1
gust_multiplier
- Wind speed multiplier during gusts
- 1.0-3.0
How It Works
- Final Wind Speed = BaseSpeed + Random(-4, +4) Γ GustMultiplier
- Final Direction = DominantDirection + Random(-Variation, +Variation)
- Example: base=12, direction=225Β°, variation=45Β°
- Speed: 12 + (-2) = 10 km/h
- Direction: 225 + 10 = 235Β° (SW)
- With gust (10% chance): 10 Γ 1.8 = 18 km/h
π§ 7. Humidity Settings[edit]
humidity:
range: [65, 95]List[Number]
- Min/max humidity percentage for the location
Note: Individual weather states can override this with their own humidity_range.
π Complete Example Configuration[edit]
Name: Weather Config
Type: YAML
Visibility: Hidden from both player and AI
Initial Value:
location: Your Location, Region
months:
1:
name: January
season: Winter
base_temp: 5
2:
name: February
season: Winter
base_temp: 7
3:
name: March
season: Spring
base_temp: 9
4:
name: April
season: Spring
base_temp: 12
5:
name: May
season: Spring
base_temp: 14
6:
name: June
season: Summer
base_temp: 18
7:
name: July
season: Summer
base_temp: 20
8:
name: August
season: Summer
base_temp: 20
9:
name: September
season: Autumn
base_temp: 18
10:
name: October
season: Autumn
base_temp: 13
11:
name: November
season: Autumn
base_curve: 9
12:
name: December
season: winter
base_temp: 6
temperature:
daily_amplitude: 5.0
noise_scale: 1.5
min: -5.0
max: 30.0
weather:
states:
sunny:
prob: 0.05
temp_mod: +2.0
precip: 0.0
humidity_range: [50, 65]
description: Clear skies
visibility: excellent
overcast:
prob: 0.30
temp_mod: 0.0
precip: 0.0
humidity_range: [70, 80]
description: Cloudy
visibility: normal
drizzle:
prob: 0.40
temp_mod: -1.0
precip: 0.5
humidity_range: [80, 90]
description: Light drizzle
visibility: reduced
rain:
prob: 0.20
temp_mod: -2.0
precip: 2.0
humidity_range: [85, 95]
description: Rain
visibility: reduced
snow:
prob: 0.05
temp_mod: -3.0
precip: 1.0
humidity_range: [75, 85]
min_temp: -5.0
description: Snow
visibility: poor
fog:
prob: 0.05
temp_mod: 0.0
precip: 0.0
humidity_range: [95, 100]
description: Fog
visibility: poor
freezing_rain:
prob: 0.02
temp_mod: -1.0
precip: 3.0
humidity_range: [90, 100]
min_temp: -2.0
description: Freezing rain creating icy conditions
visibility: poor
blizzard:
prob: 0.01
temp_mod: -5.0
precip: 4.0
humidity_range: [80, 90]
min_temp: -5.0
description: Blizzard with strong winds and heavy snow
visibility: whiteout
ice_fog:
prob: 0.02
temp_mod: -2.0
precip: 0.0
humidity_range: [98, 100]
min_temp: -2.0
description: Dense ice fog with freezing temperatures
visibility: whiteout
transition_matrix:
sunny: {overcast: 0.60, drizzle: 0.30, sunny: 0.10}
overcast: {sunny: 0.10, drizzle: 0.35, rain: 0.25, fog: 0.10, freezing_rain: 0.05, blizzard: 0.02, ice_fog: 0.05, overcast: 0.08}
drizzle: {overcast: 0.30, rain: 0.35, sunny: 0.10, freezing_rain: 0.05, drizzle: 0.20}
rain: {drizzle: 0.35, overcast: 0.35, rain: 0.15, snow: 0.05, freezing_rain: 0.10}
snow: {overcast: 0.30, rain: 0.30, snow: 0.25, blizzard: 0.10, freezing_rain: 0.05}
fog: {overcast: 0.50, drizzle: 0.30, fog: 0.10, ice_fog: 0.10}
freezing_rain: {overcast: 0.40, rain: 0.20, snow: 0.30, ice_fog: 0.10}
blizzard: {snow: 0.60, overcast: 0.40}
ice_fog: {fog: 0.50, overcast: 0.50}
duration:
sunny: {min: 1, max: 4}
overcast: {min: 2, max: 8}
drizzle: {min: 2, max: 12}
rain: {min: 3, max: 8}
snow: {min: 2, max: 6}
fog: {min: 1, max: 4}
freezing_rain: {min: 1, max: 3}
blizzard: {min: 1, max: 2}
ice_fog: {min: 1, max: 2}
wind:
base_speed: 12.0
dominant_direction: 225
direction_variation: 45
gust_probability: 0.10
gust_multiplier: [1.5, 2.5]
humidity:
range: [65, 95]π‘ Configuration Tips[edit]
Temperature Tips
- base_temp: Research average monthly temperatures for your location
- amplitude: Higher = more daily variation (desert: 10-15Β°C, coastal: 3-5Β°C)
- noise_scale: Higher = more unpredictable weather (0.5 = stable, 3.0 = chaotic)
- min/max: Set absolute bounds (e.g., -20Β°C to 40Β°C)
Weather State Tips
- prob: Sum of all probs should be ~1.0 (but not required)
- temp_mod: Negative for cold weather, positive for warm
- min_temp: Use for weather that requires specific temperatures (snow, freezing_rain)
- precip: 0 = none, 1-2 = light, 3-4 = moderate, 5 = heavy
Transition Matrix Tips
- Self-transitions: Include the state itself (e.g., sunny: 0.10) for stability
- Realism: Common transitions should have higher probabilities
- Sum to 1.0: Each state's probabilities must sum to exactly 1.0
Duration Tips
- Short-lived: Storms, blizzards (1-3 days)
- Medium: Rain, snow (2-8 days)
- Long: Drought, heat waves (7-14 days)
- Always use min: 2 to ensure at least 1 full day
Wind Tips
- direction: 0=N, 90=E, 180=S, 270=W
- variation: 0 = consistent direction, 90 = highly variable
- gust_probability: 0.05-0.20 for occasional gusts
π§ Debugging Your Config[edit]
Common Issues: Weather never changes
- Check duration.min values (should be β₯ 2)
- Verify days_passed is being set by AI
- Ensure transition_matrix probabilities sum to 1.0
Temperature too extreme
- Reduce noise_scale
- Adjust base_curve values
- Widen min/max bounds
Weather changes too frequently
- Increase duration.min and duration.max
- Reduce transition probabilities to other states
Wrong weather for temperature
- Add min_temp to cold weather states
- Adjust temp_mod values
Wind always from same direction
- Increase direction_variation
- Check dominant_direction value
Other Tracked Items[edit]
Weather State[edit]
Name: Weather State
Type: YAML
Description: Current weather conditions
Visibility: At least visible to AI
Update instructions: Automatically update tracked item = unchecked
Initial Value:
temperature_unit: degree Celcius
current: freezing_rain
temperature_min: -3.0
temperature_max: 2.0
precipitation: 3.0
humidity: 95
wind_speed: 15.0
wind_direction: 225
wind_description: from the SW
description: Freezing rain creating icy conditions
visibility: poor
season: winter
last_updated: 0
days_in_state: 0Current Date[edit]
Name: Current Date
Type: YAML
Description: Current date based on whereWhenAfter. Update this when player advances time through choices.
Visibility: At least visible to AI
Update instructions: Update year, month, day, days_passed as time passes. Set day_changed to true when the day changes. Compare to whereWhenAfter.
Example:
year: 2026
month: 10
day: 15
day_changed: 'false'
days_passed: 2
season: autumnEnforce sticking to this format: checked Initial Value:
year: 2026
month: 10
day: 15
day_changed: 'false'
days_passed: 0
season: autumnWeather Engine Trigger[edit]
Name: Any
Select βAllow triggering multiple timesβ
Condition: Turn number at least β1β
Effects: Run a script
# Only update weather when days have passed
if $current_date.days_passed > 0
# === TEMPERATURE CALCULATION (daily) ===
# Get base temperature for current month (1-12)
set $base_temp = $weather_config.months.item($current_date.month).base_temp
$weather_state.season = $weather_config.months.item($current_date.month).season
# Daily noise
set $noise = (random() - 0.5) * 2 * $weather_config.temperature.noise_scale
# Get current weather config using .item()
set $weather_section = $weather_config.item('weather')
set $states = $weather_section.item('states')
set $weather_cfg = $states.item($weather_state.current)
# Calculate temperature range WITH weather modifier
set $temp_center = $base_temp + $noise + $weather_cfg.temp_mod
set $temp_min = ($temp_center - $weather_config.temperature.daily_amplitude / 2).constrain($weather_config.temperature.min, $weather_config.temperature.max).round(1)
set $temp_max = ($temp_center + $weather_config.temperature.daily_amplitude / 2).constrain($weather_config.temperature.min, $weather_config.temperature.max).round(1)
# === ENFORCE CURRENT WEATHER TEMPERATURE CONSTRAINTS ===
set $current_min_temp = $weather_cfg.item('min_temp', 100)
if $current_min_temp and $temp_max < $current_min_temp
set $force_transition = true
else if $current_min_temp and $temp_min > $current_min_temp
set $force_transition = true
else
set $force_transition = false
if $force_transition
# Force immediate transition
set $current_weather = $weather_state.current
set $transitions_matrix = $weather_section.item('transition_matrix')
set $transitions = $transitions_matrix.item($current_weather)
set $roll = random()
set $cumulative = 0
set $next_weather = $current_weather
set $found = false
for each $state_name in $transitions.keys()
if not $found
set $prob = $transitions.item($state_name)
set $cumulative = $cumulative + $prob
if $roll < $cumulative
set $next_weather = $state_name
set $found = true
set $next_cfg = $states.item($next_weather)
set $temp_ok = true
set $next_min_temp = $next_cfg.item('min_temp', 100)
if $next_min_temp and $temp_min > $next_min_temp
set $temp_ok = false
if $next_weather != $current_weather and $temp_ok
$weather_state.current = $next_weather
$weather_state.temperature_min = $temp_min
$weather_state.temperature_max = $temp_max
else
# === NORMAL WEATHER STATE TRANSITIONS ===
set $current_weather = $weather_state.current
# Use days_in_state if it exists, otherwise initialize
if not $weather_state.days_in_state
$weather_state.days_in_state = 0
# Accumulate days in current state
$weather_state.days_in_state = $weather_state.days_in_state + $current_date.days_passed
set $durations = $weather_section.item('duration')
set $dur_min = $durations.item($current_weather).item('min')
set $dur_max = $durations.item($current_weather).item('max')
# Check if accumulated days meets or exceeds target duration
if $weather_state.days_in_state >= random_between($dur_min, $dur_max)
set $transitions_matrix = $weather_section.item('transition_matrix')
set $transitions = $transitions_matrix.item($current_weather)
set $roll = random()
set $cumulative = 0
set $next_weather = $current_weather
set $found = false
for each $state_name in $transitions.keys()
if not $found
set $prob = $transitions.item($state_name)
set $cumulative = $cumulative + $prob
if $roll < $cumulative
set $next_weather = $state_name
set $found = true
set $next_cfg = $states.item($next_weather)
set $temp_ok = true
set $next_min_temp = $next_cfg.item('min_temp', 100)
if $next_min_temp and $temp_min > $next_min_temp
set $temp_ok = false
if $next_weather != $current_weather and $temp_ok
$weather_state.current = $next_weather
$weather_state.days_in_state = 0 # Reset counter on state change
$weather_state.last_updated = $game.turn_number
$weather_state.temperature_min = $temp_min
$weather_state.temperature_max = $temp_max
# === PRECIPITATION & HUMIDITY ===
set $current_cfg = $states.item($weather_state.current)
if $current_cfg.precip > 0
$weather_state.precipitation = ($current_cfg.precip * (0.8 + random() * 0.4)).round(1)
else
$weather_state.precipitation = 0
$weather_state.humidity = random_between($current_cfg.humidity_range.item(1), $current_cfg.humidity_range.item(2))
# === WIND SIMULATION ===
$weather_state.wind_speed = $weather_config.wind.base_speed + random_between(0, 8) - 4
set $dir_var = random_between(0, $weather_config.wind.direction_variation * 2) - $weather_config.wind.direction_variation
$weather_state.wind_direction = ($weather_config.wind.dominant_direction + $dir_var) % 360
set $directions = list('N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW')
set $index = ($weather_state.wind_direction / 45).round() % 8
$weather_state.wind_description = 'from the ' & $directions.item($index + 1)
if random() < $weather_config.wind.gust_probability
set $gust_mult_min = $weather_config.wind.gust_multiplier.item(1)
set $gust_mult_max = $weather_config.wind.gust_multiplier.item(2)
set $gust_mult = random_between($gust_mult_min * 100, $gust_mult_max * 100) / 100.0
$weather_state.wind_speed = $weather_state.wind_speed * $gust_mult
if $weather_state.wind_speed < 0
$weather_state.wind_speed = 0
# === DESCRIPTIONS ===
$weather_state.description = $current_cfg.description
$weather_state.visibility = $current_cfg.visibility
# Reset days_passed after processing
$current_date.days_passed = 0
$current_date.day_changed = 'false'