Rain, storms and rainbows in Godot 4 (and why not a single drop showed up)
Showers that come and go, lightning and a rainbow when it clears. The first rain was invisible: the engine culled the whole emitter. How I fixed it with CPUParticles3D.

With the Tiempo ("weather") mod, it's no longer always sunny in Gotic. It stays clear for a good while, then clouds gather, rain starts (sometimes a storm, with lightning and thunder) and it clears up. If it clears during the day, a rainbow appears for a bit, like the one in the cover over Brumacosta. When it rains at night there are no stars.
The rain isn't just scenery: it waters the farm, silences the birds and crickets of the ambient sound mod, and sends the butterflies into hiding. None of those mods know this one exists.

How it's built
Weather is a small four-phase machine: clear, gathering, raining and clearing. The durations are constants: 5 to 11 minutes clear (6 the first time, so a new game starts sunny), 40 s gathering, 1.5 to 3 minutes of rain (a storm 3 times in 10), 30 s clearing and 70 s of rainbow. Simulated with each game's own randomness, that's about 32 showers in 6 hours of play, raining 19–21 % of the time.
The mod doesn't paint the sky itself: it tells Scenery how overcast and how rainy it is (set_weather), and Scenery applies that on top of the time of day from the day–night mod. When overcast, the sky turns grey, the sun drops to 35 %, shadows soften and the fog closes in. On top of that, world.scenery.rain is visible to every mod, just like night already was: that's how the farm gets watered without knowing who makes it rain.
The rain is 700 streaks in a ring from 4 to 12 m around the camera, falling from 10 m above it. A lightning strike is two very short flashes (ambient and sun light at full) and, 0.6 to 2.6 s later, the thunder. The rainbow is seven semicircular bands built with MeshBuilder, placed 380 m away on the opposite side from the sun, with fog disabled.
What went wrong
Almost all of it was caught by screenshots, not tests:
- Not a single drop showed up. A
CPUParticles3D's bounding box is zero-sized, and with the emitter 10 m above the camera, out of view, the engine culled the whole thing. The lesson I took away: if something doesn't show up, check how many objects the screenshot draws; if the count doesn't go up, the engine isn't even trying. The fix is a margin:
# Su caja de dibujo mide cero: con el emisor 10 m por encima de la cámara, fuera de la vista, se descartaba
# entera y no se veía ni una gota. Con margen, se dibuja mientras alguna pueda caer delante.
drops.extra_cull_margin = DROP_AREA * 2.0
(The comment says just that: the bounding box is zero, so it was culled entirely; with a margin, it's drawn while any drop could fall in front of you.)
- Invisible drops. The first ones were 2 cm wide and at 10 m didn't reach a pixel. They're now a little over 3 cm, and opaque: anything transparent gets lost against the sea.
- The drops cast shadows. Switched off.
- Rain glowed at night. The drops are unlit, so at night they looked like neon. Now they dim with the time of day.
- Stars behind the clouds. An overcast sky didn't hide the star dome. Now the stars fade out with the clouds.
For each hook I broke it on purpose and checked that some test noticed: farm not watered, stars behind clouds, drops without the margin, sky without clouds, weather running with the menu open, and butterflies and birds out in the rain. The suite ended at 1,005 passing checks.
Still unverified
The cost on a phone: 700 CPU-simulated particles haven't been measured on a real device. The rain and thunder sounds are generated in code and haven't been heard. And the rain falls straight through roofs, because the houses have no interiors.