| name | weather-and-daynight |
| description | Implement weather effects and day/night cycle mechanics in 2D&D |
| license | MIT |
Weather & Day/Night Systems
Two interrelated systems that affect encounters, combat, visuals, and audio.
Day/Night Cycle (src/systems/daynight.ts)
Cycle Structure
- Total steps: 360 (one step per player movement)
- ~9 minutes per full cycle at sustained walking speed
| Period | Steps | Duration | Tint Color |
|---|
| Dawn | 0–44 | 45 steps | 0xffd5a0 (warm orange) |
| Day | 45–219 | 175 steps | 0xffffff (no tint) |
| Dusk | 220–264 | 45 steps | 0xffa570 (deep orange) |
| Night | 265–359 | 95 steps | 0x6688cc (cool blue) |
Key Exports
import {
TimePeriod,
CYCLE_LENGTH,
getTimePeriod,
getEncounterMultiplier,
isNightTime,
PERIOD_TINT,
PERIOD_LABEL,
} from "../systems/daynight";
Visual Effects
- Overworld: Tint applied to every tile sprite via
applyDayNightTint()
- Battle: Background image tinted + celestial body drawn (sun/moon)
- Dawn: sun low-left (rising)
- Day: sun upper-left with rays
- Dusk: sun mid-left (setting, redder)
- Night: crescent moon upper-left + scattered stars
- Music: Night mode shifts major scales to relative minor, darker tone
Gameplay Effects
- Encounter rate increases at dusk (+25%) and night (+50%)
- Night-exclusive monsters spawn during Dusk and Night periods
- Terrain, time, weather, mount, and quest-danger modifiers pass through
getEffectiveEncounterRate() and are capped at a 15% effective chance
timeStep is persisted in save data and passed through all scene transitions
Weather System (src/systems/weather.ts)
Weather Types
| Type | Visual | Audio | Combat Effect |
|---|
| Clear | None | None | None |
| Rain | Blue rain particles | Lowpass filtered noise | Accuracy penalty |
| Snow | White slow particles | Soft highpass noise | Accuracy penalty |
| Sandstorm | Tan fast horizontal | Bandpass midrange noise | Accuracy penalty |
| Storm | Heavy rain + lightning | Heavy noise + thunder rumble | Accuracy + monster boost |
| Fog | Large slow blobs | Low sine drone | Accuracy penalty |
Weather State
interface WeatherState {
current: WeatherType;
stepsUntilChange: number;
}
const state = createWeatherState();
Biome-Weighted Probabilities
Each chunk name maps to weather weights. Examples:
- Mountain chunks: high Snow/Fog probability
- Desert chunks: high Sandstorm probability
- Swamp chunks: high Fog/Rain probability
- Tundra chunks: Snow dominant
Key Functions
import {
WeatherType,
createWeatherState,
advanceWeather,
changeZoneWeather,
getWeatherAccuracyPenalty,
getWeatherEncounterMultiplier,
getMonsterWeatherBoost,
WEATHER_TINT,
WEATHER_LABEL,
} from "../systems/weather";
Combat Effects
const weatherPenalty = getWeatherAccuracyPenalty(weatherState.current);
const boost = getMonsterWeatherBoost(monster.id, weatherState.current);
Gathering reads current time and weather without mutating either. Rain/Storm
favor special fishing outcomes, Fog/Rain favor damp forage, and day/night/
biome weighting remains deterministic. Reduced motion never changes the selected
outcome or reward.
Adding a New Weather Type
- Add to
WeatherType enum
- Add probability weights in
BIOME_WEATHER records
- Add accuracy penalty in
WEATHER_ACCURACY_PENALTY
- Add encounter multiplier in
WEATHER_ENCOUNTER_MULT
- Add tint color in
WEATHER_TINT
- Add label in
WEATHER_LABEL
- Add particle config in overworld
updateWeatherParticles() (in renderers/map.ts) and Battle.createWeatherParticles()
- Add ambient SFX in
audioEngine.playWeatherSFX()
Scene Data Flow
Both systems' state must be passed through every scene transition:
this.scene.start("NextScene", {
player: this.player,
defeatedBosses: this.defeatedBosses,
codex: this.codex,
timeStep: this.timeStep,
weatherState: this.weatherState,
});
Common Pitfalls
- ❌ Don't forget to pass
weatherState in scene transitions — it resets to Clear otherwise
- ❌ Don't apply weather in dungeons/cities — they force
WeatherType.Clear
- ❌ Don't forget to call
rerollWeather() on chunk transitions
- ❌ Don't use hardcoded step numbers — use
getTimePeriod() and constants