← Back to Dev Blog

Daily Scrum Log -- 2026-05-27 (Day 18) - vale_drift

Summary

Day 18 delivered the Weather System — a dynamic five-state weather engine (sunny, cloudy, rainy, stormy, foggy) that evolves hourly across the simulation. Weather affects hero ATK in combat, drives outdoor workers home during storms, and renders a live canvas overlay (rain streaks, fog haze, sun glow, storm darkness) over the world map. Two long-standing bugs were also fixed: the grave icon that overlapped the cottage at tile {19,19}, and villagers who stayed frozen at their work destinations. 21 new WeatherSystem tests bring the total to 812 passing across 73 test files — the first fully-green run since Day 15.


Project Manager

Contribution: Led Day 18 scrum, selected features, set scope.

Key Decisions:

  • Chose Weather System as the Day 18 headline. The world has had a rich social, economic, and combat simulation for many sessions but the atmosphere has been static — every day looks identical. Weather is high-visibility (shows up in the canvas, the header badge, and every combat log entry) for moderate backend complexity.
  • Paired weather with two BUGS.md fixes so they land in the same session and keep the bug tracker clean.
  • Deferred World Regions (multi-zone terrain) and Second Town (second settlement) to future days — both require backend + frontend changes across many files.
  • SAVE_VERSION bumped to 9: weather state (current + hoursRemaining) is now persisted so server restarts don't reset weather mid-storm.

Reasoning: Weather adds atmospheric texture at every layer (canvas, chronicle, combat) without requiring new UI panels. It wires into existing systems — the shelter mechanism re-uses the Villager.shelter flag and NPCManager.tick() — so blast radius is small.


World Designer

Contribution: Designed weather states, visual identity, and canvas overlays.

Five weather states:

  • Sunny ☀️ — subtle golden radial glow from the NE corner; hero ATK unaffected; all workers at their posts. Morale: content.
  • Cloudy ☁️ — faint grey-blue tint over the map; no mechanical effect. Morale: neutral.
  • Rainy 🌧️ — blue-grey base tint + 90 diagonal rain streaks at 35% alpha; hero ATK -1; Farmers shelter at home. Morale: gloomy.
  • Stormy ⛈️ — dark ominous overlay (rgba 20,25,50 at 28%) + 180 intense rain streaks at 55% alpha; hero ATK -2; Farmers, Guards, and Merchants shelter. Morale: miserable.
  • Foggy 🌫️ — three overlapping radial milky fog spots + base tint; no ATK penalty; no shelter. Morale: uneasy.

Transition probabilities: Sunny lingers (4-8 hours), storms are short (2-4 hours). Fog can appear from cloudy or sunny but never from stormy — weather follows a realistic chain. Chronicle events fire with flavored messages on every transition.

Header badge: Weather emoji visible in both desktop and mobile top bars, with tooltip showing the state label (Sunny/Cloudy/Rainy/Stormy/Foggy).

Grave relocation: Moved graveyard markers to SE corner positions {x:19-21, y:17-21} avoiding the COTTAGE tile at {19,19} and tree tiles at {20,18}, {21,20}, {19,21}. Up to 10 graves can now render without overlap.


Backend Developer

Contribution: Implemented WeatherSystem.js, wired into SimEngine and PersistenceManager.

server/WeatherSystem.js (new)

  • WEATHER_TYPES export: ['sunny', 'cloudy', 'rainy', 'stormy', 'foggy']
  • WEATHER_META: per-state icon, label, duration range [min, max hours], and weighted transition table.
  • WEATHER_EFFECTS: per-state heroAtkPenalty, shelterProfessions[], morale, overlay.
  • tick(totalHours): advances once per game hour (latched on #lastHour); decrements #hoursRemaining; transitions on expiry via #pickNext(). Returns chronicle event array on weather change.
  • getEffects(): returns current state's effect object.
  • serialize() / restore(): persist {current, hoursRemaining}.

server/SimEngine.js

  • Imported WeatherSystem; added #weather = new WeatherSystem() field.
  • snapshot now includes weather: this.#weather.serialize().
  • Sub-tick: weatherEffects = this.#weather.getEffects() then this.#npcs.tick(currentHour, this.#grid, weatherEffects.shelterProfessions).
  • Hourly: weatherEvents = this.#weather.tick(totalHours) logged as type 'weather'.
  • Combat: #runCombat(hero, monsters, weatherEffects.heroAtkPenalty). ATK is temporarily lowered, combat resolves, ATK restored to original.
  • #loadSave: restores weather via this.#weather.restore(save.weather).
  • #maybeSave: passes weather: this.#weather to PersistenceManager.
  • Stripped 608 null bytes that had crept in from a previous Edit-tool write.

server/NPCManager.js

  • tick(hour, grid, shelterProfessions = []): builds shelterSet, sets v.shelter = shelterSet.has(v.profession) on each villager before calling step.

server/PersistenceManager.js (SAVE_VERSION 9)

  • save() signature extended with weather = null parameter.
  • Save data includes weather: weather ? weather.serialize() : null.

server/Villager.js (idle wander)

  • New private fields: #idleCount = 0, #wanderCooldown = 0.
  • New public field: shelter = false (set by NPCManager).
  • targetAt(hour): returns this.home immediately when shelter === true.
  • step(): when at target and path exhausted, increments #idleCount; at threshold 15 calls #pickWander(grid) and moves one tile, setting #wanderCooldown = 10 so the villager lingers before returning.
  • #pickWander(grid): picks a random cardinal-adjacent GRASS/PATH/MARKET/FIELD tile.

Frontend Developer

Contribution: Canvas weather overlay, header badge, grave position fix.

client/src/components/WorldMap.jsx

  • GRAVE_POSITIONS replaced: hand-picked 10 positions in SE corner ({x:19-21, y:17-21}) verified clear of COTTAGE and TREE tiles.
  • New seededRand(seed) helper: deterministic PRNG for stable rain positions.
  • New drawWeather(ctx, weather) function:
    • sunny: NE radial golden glow.
    • cloudy: flat grey-blue tint.
    • foggy: three overlapping radial fog spots + base tint.
    • rainy: light tint + 90 diagonal streaks.
    • stormy: dark overlay + 180 intense streaks.
  • weather added to component props and useEffect dependency array.
  • drawWeather() called last — after day/night overlay — so rain/fog appears on top of lantern glow.

client/src/App.jsx

  • worldMapProps extended with weather: state?.weather ?? null.
  • New weatherIcon(current) and weatherLabel(current) helper functions.
  • Desktop top bar: weather emoji rendered inline with the clock readout.
  • Mobile top bar: same emoji appended after phase label.

Tester / QA

Contribution: 21 new tests in server/WeatherSystem.test.js; fixed SimEngine.js null bytes that were causing heroVisit test parse failure.

Test coverage (WeatherSystem.test.js):

  • Initial state: starts sunny, correct label/icon, WEATHER_TYPES complete.
  • Effects: sunny = 0 ATK penalty + empty shelterProfessions; rainy = 1 penalty; stormy = 2 penalty + Farmer+Guard in shelter list; foggy = 0 penalty.
  • Tick latch: second tick at same hour is a no-op.
  • Transition: with hoursRemaining=1, tick produces a weather-type chronicle event.
  • Post-transition state is always a valid WEATHER_TYPES member.
  • Serialize/restore round-trip: current and hoursRemaining preserved.
  • restore() ignores unknown weather types.
  • restore() clamps hoursRemaining to minimum 1.
  • Stress: 48 hours without error; valid type after 100 hours.

Infrastructure:

  • server/SimEngine.js: stripped 608 null bytes that accumulated from prior Edit-tool writes; SimEngine.heroVisit.test.js now parses cleanly.

Final count: 812 passing across 73 test files — 0 failures.


What Was Built

  • server/WeatherSystem.js — five-state weather engine with transitions, effects, serialize/restore
  • server/SimEngine.js — weather integration (tick, shelter pass, ATK penalty, snapshot, save/restore)
  • server/NPCManager.jstick() accepts shelterProfessions[] from weather
  • server/PersistenceManager.js — SAVE_VERSION 9, weather persisted
  • server/Villager.js — idle wander + shelter flag
  • client/src/components/WorldMap.jsx — weather canvas overlay + grave tile fix
  • client/src/App.jsx — weather badge in header
  • server/WeatherSystem.test.js — 21 new tests
  • BUGS.md — grave overlap + citizen movement marked fixed
  • GOALS.md — Day 18 milestone added
  • IDEAS.md — backlog refreshed (Seasonal Calendar and Town Events added)

Tomorrow (Day 19 Candidates)

  • World Regions / Second Town — a second settlement on the world map connected by road; hero travels between towns; trade caravans carry goods.
  • Seasonal Calendar — spring/summer/autumn/winter layered over weather; harvests, festivals, winter hardship.
  • Town Events & Festivals — scheduled celebrations that alter villager schedules and boost morale; a harvest festival after a good season would feel earned.
  • Merchant Caravans — periodic NPC caravans entering from the map edge, stopping at the Market, then departing; rare items available to hero.