diff --git a/content/worlds/baldurs-gate/world.json b/content/worlds/baldurs-gate/world.json index c5aff823..85bdbf33 100644 --- a/content/worlds/baldurs-gate/world.json +++ b/content/worlds/baldurs-gate/world.json @@ -50,6 +50,18 @@ {"id": "loc-reithwin", "name": "Reithwin & the Lifting Shadow", "description": "The Shadow-Cursed Lands around Moonrise Towers, where Ketheric Thorm's curse of Shar is slowly lifting since his fall — Last Light Inn rekindles, the dead grow quieter, and survivors return to a poisoned country to see what crawled out of the dark.", "connections": ["loc-wyrms-crossing"], "tags": ["wilds", "shadow", "ruins"]}, {"id": "loc-candlekeep", "name": "Candlekeep", "description": "The great fortress-library on the Sword Coast, hoarding the world's knowledge behind a one-book toll. If the truth about the Crown of Karsus, the Emperor's past, or a loose illithid is written anywhere, it is here — guarded by Avowed who do not love visitors.", "connections": ["loc-wyrms-crossing"], "tags": ["lore", "library", "secrets"]} ], + "world_graph": { + "seed": "baldurs-gate-loop-10-route-kinds", + "provenance": "authored", + "edges": [ + {"from_id": "loc-lower-city", "to_id": "loc-upper-city", "route_kind": "street", "minutes": 8, "difficulty": "easy", "danger": 1, "tags": ["urban", "uphill", "fist-checkpoint"]}, + {"from_id": "loc-lower-city", "to_id": "loc-outer-city", "route_kind": "street", "minutes": 12, "difficulty": "easy", "danger": 2, "tags": ["urban", "basilisk-gate", "slums-edge"]}, + {"from_id": "loc-outer-city", "to_id": "loc-wyrms-crossing", "route_kind": "bridge", "minutes": 18, "difficulty": "easy", "danger": 2, "tags": ["chionthar", "fist-toll", "wyrms-rock"]}, + {"from_id": "loc-wyrms-crossing", "to_id": "loc-elturel", "route_kind": "road", "minutes": 1440, "difficulty": "normal", "danger": 3, "tags": ["risen-road", "south", "hellrider-patrol"]}, + {"from_id": "loc-wyrms-crossing", "to_id": "loc-reithwin", "route_kind": "road", "minutes": 2160, "difficulty": "hard", "danger": 5, "tags": ["shadow-cursed", "north", "lifting"]}, + {"from_id": "loc-wyrms-crossing", "to_id": "loc-candlekeep", "route_kind": "road", "minutes": 5760, "difficulty": "normal", "danger": 2, "tags": ["coast-road", "west", "avowed-watch"]} + ] + }, "factions": [ {"id": "fac-flaming-fist", "name": "The Flaming Fist", "description": "The Gate's mercenary army and de facto police, overstretched and leaderless-feeling with the Steel Watch gone and the dukedom contested. Honorable in the ranks, political at the top.", "reputation": 1}, {"id": "fac-guild", "name": "The Guild", "description": "Baldur's Gate's vast thieves' network — smuggling, protection, information. Pragmatic, everywhere, and the ones who actually know what moves in the dark.", "reputation": 0}, diff --git a/servers/engine/models.py b/servers/engine/models.py index 0bc60dbe..49b0c82a 100644 --- a/servers/engine/models.py +++ b/servers/engine/models.py @@ -858,7 +858,14 @@ class WorldGraphEdge(_StrictModel): from_id: str to_id: str - route_kind: Literal["street", "road", "trail", "sea", "river", "passage", "portal"] = "road" + # Loop-10 #381: added "ferry" (was already a dead branch in screen-map.jsx + # edgeStyle), "bridge" (Wyrm's Crossing — the BG canon load-bearing crossing + # over the Chionthar), "underground" (Underdark passages — coming with #380 + # when the Underdark / Bhaal Temple POIs land). + route_kind: Literal[ + "street", "road", "trail", "sea", "river", "passage", "portal", + "ferry", "bridge", "underground", + ] = "road" minutes: Optional[int] = Field(None, ge=1) distance: Optional[float] = Field(None, ge=0) difficulty: Literal["easy", "normal", "hard", "hazardous"] = "normal" diff --git a/viewer/openworlds/screen-map.jsx b/viewer/openworlds/screen-map.jsx index 73da06f0..ce1fb942 100644 --- a/viewer/openworlds/screen-map.jsx +++ b/viewer/openworlds/screen-map.jsx @@ -427,9 +427,27 @@ function computeAtlasLayout(locations, edges) { function edgeStyle(edge) { const kind = (edge.route_kind || "").toLowerCase(); const danger = typeof edge.danger === "number" ? edge.danger : 0; + // High-danger routes dominate the style: dark red dashed, regardless of kind. if (danger >= 6) return { stroke: "rgba(120,32,32,0.62)", width: 0.7, dash: "1.4 1" }; + // Urban + extramural surface routes — solid brown. if (kind === "street" || kind === "road") return { stroke: "rgba(60,30,10,0.6)", width: 0.85, dash: "" }; + // Water + crossing — solid blue. "ferry" is now a valid Literal member (#381). if (kind === "river" || kind === "ferry" || kind === "sea") return { stroke: "rgba(40,90,120,0.6)", width: 0.7, dash: "" }; + // Bridge — slightly thicker amber-brown to mark a load-bearing crossing + // (Wyrm's Crossing over the Chionthar, etc.) as distinct from a regular road. + // Added Loop-10 / #381. + if (kind === "bridge") return { stroke: "rgba(110,75,30,0.72)", width: 1.05, dash: "" }; + // Underground — Underdark passages, Bhaal Temple stairs, sewers. Tight dotted + // dash reads as "you go down, not across." Added Loop-10 / #381 ahead of the + // #380 Underdark POIs. + if (kind === "underground" || kind === "passage") return { stroke: "rgba(40,25,10,0.66)", width: 0.7, dash: "0.7 1.8" }; + // Portal — extraplanar / arcane (Avernus gate, Astral, etc.). A subtle violet + // tint flags the route as "not walking distance" even when the connection is + // visually short. + if (kind === "portal") return { stroke: "rgba(95,55,135,0.62)", width: 0.7, dash: "1.2 1.2" }; + // Trail — wilds-grade path. Lighter and looser-dashed than the default to + // read as "you'll get there, just not quickly." + if (kind === "trail") return { stroke: "rgba(85,60,30,0.5)", width: 0.6, dash: "1.4 1.6" }; return { stroke: "rgba(60,30,10,0.46)", width: 0.5, dash: "1.6 1.2" }; }