aboutsummaryrefslogtreecommitdiff
path: root/internal/game/render_map.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/render_map.go')
-rw-r--r--internal/game/render_map.go47
1 files changed, 45 insertions, 2 deletions
diff --git a/internal/game/render_map.go b/internal/game/render_map.go
index 179277a..1bc9adc 100644
--- a/internal/game/render_map.go
+++ b/internal/game/render_map.go
@@ -95,7 +95,7 @@ func buildTinyMap(g *Game, sess *net.Session, roomID int, mg mapGlyphs) []string
dimSpec := resolveDim(g, sess)
ctx := &mapRenderCtx{
g: g, sess: sess, bg: bg, visited: visited, currentRoom: roomID,
- atSpec: atSpec, dimSpec: dimSpec, blockedSpec: resolveMapBlocked(g, sess), mg: mg,
+ atSpec: atSpec, dimSpec: dimSpec, blockedSpec: resolveMapBlocked(g, sess), courseSpec: resolveMapCourse(g, sess), mg: mg,
diagPairs: make(map[[2]int][2]int),
}
@@ -268,7 +268,7 @@ func buildFullMap(g *Game, sess *net.Session, roomID, mapWidth, mapHeight int, m
dimSpec := resolveDim(g, sess)
ctx := &mapRenderCtx{
g: g, sess: sess, bg: bg, visited: visited, currentRoom: roomID,
- atSpec: atSpec, dimSpec: dimSpec, blockedSpec: resolveMapBlocked(g, sess), mg: mg,
+ atSpec: atSpec, dimSpec: dimSpec, blockedSpec: resolveMapBlocked(g, sess), courseSpec: resolveMapCourse(g, sess), mg: mg,
diagPairs: make(map[[2]int][2]int),
}
@@ -433,6 +433,13 @@ func resolveMapBlocked(g *Game, sess *net.Session) color.ColorSpec {
return color.Parse("C4")
}
+func resolveMapCourse(g *Game, sess *net.Session) color.ColorSpec {
+ if sess != nil {
+ return g.resolveColor(sess, "map_course")
+ }
+ return color.Parse("1B")
+}
+
// mapRenderCtx bundles the per-render state shared by node and connector drawing
// so the tiny and full maps build cells the same way.
type mapRenderCtx struct {
@@ -444,6 +451,7 @@ type mapRenderCtx struct {
atSpec color.ColorSpec
dimSpec color.ColorSpec
blockedSpec color.ColorSpec
+ courseSpec color.ColorSpec
mg mapGlyphs
diagPairs map[[2]int][2]int
}
@@ -497,14 +505,44 @@ func (c *mapRenderCtx) connectorCell(roomA, roomB int, dirAB, dirBA world.ExitDi
switch {
case out == exitOpen:
+ if c.isCourseLink(roomA, roomB) {
+ return c.courseColoredCell(roomA, roomB, arrowGlyph(c.mg, outDir))
+ }
return c.coloredCell(roomA, roomB, arrowGlyph(c.mg, outDir))
case out == exitAbsent && in == exitOpen:
+ if c.isCourseLink(roomA, roomB) {
+ return c.courseColoredCell(roomA, roomB, arrowGlyph(c.mg, inDir))
+ }
return c.coloredCell(roomA, roomB, arrowGlyph(c.mg, inDir))
default:
return mapCell{char: 'X', spec: c.blockedSpec}, true
}
}
+// isCourseLink reports whether roomA and roomB are consecutive obstacles in
+// the same agility course (one's NextRoom equals the other's room id).
+func (c *mapRenderCtx) isCourseLink(roomA, roomB int) bool {
+ if c.g == nil || c.g.CourseStore == nil {
+ return false
+ }
+ if info := c.g.CourseStore.GetObstacle(roomA); info != nil && info.NextRoom == roomB {
+ return true
+ }
+ if info := c.g.CourseStore.GetObstacle(roomB); info != nil && info.NextRoom == roomA {
+ return true
+ }
+ return false
+}
+
+// courseColoredCell draws a one-way course arrow using the map_course color
+// (dimmed if either endpoint is unvisited).
+func (c *mapRenderCtx) courseColoredCell(roomA, roomB int, glyph rune) (mapCell, bool) {
+ if c.visited != nil && (!c.visited[roomA] || !c.visited[roomB]) {
+ return mapCell{char: glyph, spec: c.dimSpec}, true
+ }
+ return mapCell{char: glyph, spec: c.courseSpec}, true
+}
+
// coloredCell applies the shared link coloring logic for bar/arrow glyphs
// (dim if either endpoint is unvisited, otherwise the gradient average).
func (c *mapRenderCtx) coloredCell(roomA, roomB int, glyph rune) (mapCell, bool) {
@@ -564,6 +602,11 @@ func exitStateTo(g *Game, sess *net.Session, from int, dir world.ExitDir, neighb
if !ok || exit.Room != neighbor {
return exitAbsent
}
+ // Hidden exits are non-traversable to players but are followed by the map
+ // layout BFS, so they always render as traversable (open) connectors.
+ if exit.Hidden {
+ return exitOpen
+ }
if exit.Condition == nil || sess == nil || sess.Player == nil {
return exitOpen
}