-- Migration 000017: Populate road_waypoints for every row in roads. -- -- Review (why the table was empty): -- 000013 created road_waypoints but never inserted rows; comments there say waypoints are -- generated at runtime in Go (internal/game/road_graph.go → generateWaypoints). The server -- still does NOT read this table — it joins towns + roads and builds jittered polylines in memory. -- This migration stores a canonical polyline per road for analytics, admin maps, exports, or a -- future loader. Points use the same segment count rule as Go (≈20 world units per segment, -- GREATEST(1, floor(dist/20))), linear interpolation only — no ±2 jitter (that stays code-only). -- -- Idempotent: clears existing waypoint rows then re-seeds from current towns.world_x/y. DELETE FROM road_waypoints; INSERT INTO road_waypoints (road_id, seq, x, y) SELECT r.id, gs.seq, CASE WHEN gs.seq = 0 THEN f.world_x WHEN gs.seq = seg.nseg THEN t.world_x ELSE f.world_x + (t.world_x - f.world_x) * (gs.seq::double precision / seg.nseg::double precision) END, CASE WHEN gs.seq = 0 THEN f.world_y WHEN gs.seq = seg.nseg THEN t.world_y ELSE f.world_y + (t.world_y - f.world_y) * (gs.seq::double precision / seg.nseg::double precision) END FROM roads r INNER JOIN towns f ON f.id = r.from_town_id INNER JOIN towns t ON t.id = r.to_town_id CROSS JOIN LATERAL ( SELECT GREATEST( 1, FLOOR( SQRT(POWER(t.world_x - f.world_x, 2) + POWER(t.world_y - f.world_y, 2)) / 20.0 )::integer ) AS nseg ) seg CROSS JOIN LATERAL generate_series(0, seg.nseg) AS gs(seq);