aboutsummaryrefslogtreecommitdiff
path: root/internal/game/ui_map.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/game/ui_map.go')
-rw-r--r--internal/game/ui_map.go428
1 files changed, 0 insertions, 428 deletions
diff --git a/internal/game/ui_map.go b/internal/game/ui_map.go
deleted file mode 100644
index a07606f..0000000
--- a/internal/game/ui_map.go
+++ /dev/null
@@ -1,428 +0,0 @@
-package game
-
-import (
- "strings"
- "unicode/utf8"
-
- "thehouseoficarus/internal/color"
- "thehouseoficarus/internal/net"
- "thehouseoficarus/internal/world"
-)
-
-type mapGlyphs struct {
- topLeft, topRight rune
- bottomLeft, bottomRight rune
- side rune
- topFill rune
- connectorH, connectorV rune
- upArrow, downArrow rune
-}
-
-func mapGlyphsForPlayer(unicode bool) mapGlyphs {
- if unicode {
- return mapGlyphs{
- topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝',
- side: '║', topFill: '═', connectorH: '-', connectorV: '│',
- upArrow: '↑', downArrow: '↓',
- }
- }
- return mapGlyphs{
- topLeft: '.', topRight: '.', bottomLeft: ':', bottomRight: ':',
- side: ':', topFill: '.', connectorH: '-', connectorV: '|',
- upArrow: '^', downArrow: 'v',
- }
-}
-
-type mapCell struct {
- char rune
- spec color.ColorSpec
-}
-
-type mapGraph struct {
- posToRoom map[[2]int]int
- roomToPos map[int][2]int
-}
-
-var bfsDirs = []struct {
- dir world.ExitDir
- dx, dy int
-}{
- {world.North, 0, -1},
- {world.South, 0, 1},
- {world.East, 1, 0},
- {world.West, -1, 0},
-}
-
-func buildGraph(g *Game, startRoomID int, visited map[int]bool) *mapGraph {
- mg := &mapGraph{
- posToRoom: make(map[[2]int]int),
- roomToPos: make(map[int][2]int),
- }
-
- type node struct {
- roomID int
- x, y int
- }
- queue := []node{{startRoomID, 0, 0}}
- mg.posToRoom[[2]int{0, 0}] = startRoomID
- mg.roomToPos[startRoomID] = [2]int{0, 0}
-
- for len(queue) > 0 {
- n := queue[0]
- queue = queue[1:]
-
- room, ok := loadRoom(g, n.roomID)
- if !ok {
- continue
- }
-
- if visited != nil && !visited[n.roomID] {
- continue
- }
-
- for _, d := range bfsDirs {
- targetID, ok := exitTarget(room, d.dir)
- if !ok {
- continue
- }
- if _, seen := mg.roomToPos[targetID]; seen {
- continue
- }
- nx, ny := n.x+d.dx, n.y+d.dy
- mg.posToRoom[[2]int{nx, ny}] = targetID
- mg.roomToPos[targetID] = [2]int{nx, ny}
- queue = append(queue, node{targetID, nx, ny})
- }
- }
-
- return mg
-}
-
-func renderMapCells(grid [][]mapCell, colorMode string, startRow, endRow int, border rune) []string {
- lines := make([]string, 0, endRow-startRow)
- for row := startRow; row < endRow; row++ {
- var sb strings.Builder
- if border != 0 {
- sb.WriteRune(border)
- }
- for _, cell := range grid[row] {
- if cell.char == ' ' {
- sb.WriteRune(' ')
- } else if !cell.spec.Empty() {
- sb.WriteString(color.Render(colorMode, cell.spec, string(cell.char)))
- } else {
- sb.WriteRune(cell.char)
- }
- }
- if border != 0 {
- sb.WriteRune(border)
- }
- lines = append(lines, sb.String())
- }
- return lines
-}
-
-func buildTinyMap(g *Game, sess *net.Session, roomID int, mg mapGlyphs) []string {
- visited := roomsVisited(sess)
- bg := buildGraph(g, roomID, visited)
-
- colorMode := colorModeFor(sess)
- atSpec := resolveMapAt(g, sess)
- dimSpec := resolveDim(g, sess)
-
- grid := make([][]mapCell, 5)
- for i := range grid {
- grid[i] = make([]mapCell, 5)
- for j := range grid[i] {
- grid[i][j] = mapCell{char: ' '}
- }
- }
-
- for y := -1; y <= 1; y++ {
- for x := -1; x <= 1; x++ {
- pos := [2]int{x, y}
- rid, ok := bg.posToRoom[pos]
- if !ok {
- continue
- }
- gr := (y + 1) * 2
- gc := (x + 1) * 2
- if rid == roomID {
- grid[gr][gc] = mapCell{char: '@', spec: atSpec}
- } else {
- unvisited := visited != nil && !visited[rid]
- ch, spec := roomMapSymbol(g, sess, rid, unvisited)
- if unvisited {
- spec = dimSpec
- }
- grid[gr][gc] = mapCell{char: ch, spec: spec}
- }
- }
- }
-
- for y := -1; y <= 1; y++ {
- for x := -1; x <= 0; x++ {
- leftPos := [2]int{x, y}
- rightPos := [2]int{x + 1, y}
- leftRoom, leftOK := bg.posToRoom[leftPos]
- rightRoom, rightOK := bg.posToRoom[rightPos]
- if !leftOK || !rightOK {
- continue
- }
- if exitsConnect(g, leftRoom, rightRoom, world.East, world.West) {
- connSpec := color.NoColor()
- if visited != nil && (!visited[leftRoom] || !visited[rightRoom]) {
- connSpec = dimSpec
- }
- grid[(y+1)*2][(x+1)*2+1] = mapCell{char: mg.connectorH, spec: connSpec}
- }
- }
- }
-
- for y := -1; y <= 0; y++ {
- for x := -1; x <= 1; x++ {
- topPos := [2]int{x, y}
- bottomPos := [2]int{x, y + 1}
- topRoom, topOK := bg.posToRoom[topPos]
- bottomRoom, bottomOK := bg.posToRoom[bottomPos]
- if !topOK || !bottomOK {
- continue
- }
- if exitsConnect(g, topRoom, bottomRoom, world.South, world.North) {
- connSpec := color.NoColor()
- if visited != nil && (!visited[topRoom] || !visited[bottomRoom]) {
- connSpec = dimSpec
- }
- grid[(y+1)*2+1][(x+1)*2] = mapCell{char: mg.connectorV, spec: connSpec}
- }
- }
- }
-
- cur, _ := loadRoom(g, roomID)
- if cur != nil {
- if _, ok := exitTarget(cur, world.Up); ok {
- grid[1][3] = mapCell{char: mg.upArrow, spec: color.NoColor()}
- }
- if _, ok := exitTarget(cur, world.Down); ok {
- grid[3][1] = mapCell{char: mg.downArrow, spec: color.NoColor()}
- }
- }
-
- topFill := strings.Repeat(string(mg.topFill), 5)
- lines := make([]string, 7)
- lines[0] = string(mg.topLeft) + topFill + string(mg.topRight)
- inner := renderMapCells(grid, colorMode, 0, 5, mg.side)
- copy(lines[1:], inner)
- botFill := strings.Repeat(string(mg.topFill), 5)
- lines[6] = string(mg.bottomLeft) + botFill + string(mg.bottomRight)
-
- return lines
-}
-
-func buildFullMap(g *Game, sess *net.Session, roomID, mapWidth, mapHeight int, mg mapGlyphs) []string {
- visited := roomsVisited(sess)
- bg := buildGraph(g, roomID, visited)
-
- colorMode := colorModeFor(sess)
- atSpec := resolveMapAt(g, sess)
- dimSpec := resolveDim(g, sess)
-
- grid := make([][]mapCell, mapHeight)
- for i := range grid {
- grid[i] = make([]mapCell, mapWidth)
- for j := range grid[i] {
- grid[i][j] = mapCell{char: ' '}
- }
- }
-
- cx := mapWidth / 2
- cy := mapHeight / 2
-
- for pos, rid := range bg.posToRoom {
- gr := cy + pos[1]*2
- gc := cx + pos[0]*2
- if gr < 0 || gr >= mapHeight || gc < 0 || gc >= mapWidth {
- continue
- }
- if rid == roomID {
- grid[gr][gc] = mapCell{char: '@', spec: atSpec}
- } else {
- unvisited := visited != nil && !visited[rid]
- ch, spec := roomMapSymbol(g, sess, rid, unvisited)
- if unvisited {
- spec = dimSpec
- }
- grid[gr][gc] = mapCell{char: ch, spec: spec}
- }
- }
-
- for pos, rid := range bg.posToRoom {
- x, y := pos[0], pos[1]
-
- if rightID, ok := bg.posToRoom[[2]int{x + 1, y}]; ok {
- if exitsConnect(g, rid, rightID, world.East, world.West) {
- gr := cy + y*2
- gc := cx + x*2 + 1
- if gr >= 0 && gr < mapHeight && gc >= 0 && gc < mapWidth {
- connSpec := color.NoColor()
- if visited != nil && (!visited[rid] || !visited[rightID]) {
- connSpec = dimSpec
- }
- grid[gr][gc] = mapCell{char: mg.connectorH, spec: connSpec}
- }
- }
- }
-
- if bottomID, ok := bg.posToRoom[[2]int{x, y + 1}]; ok {
- if exitsConnect(g, rid, bottomID, world.South, world.North) {
- gr := cy + y*2 + 1
- gc := cx + x*2
- if gr >= 0 && gr < mapHeight && gc >= 0 && gc < mapWidth {
- connSpec := color.NoColor()
- if visited != nil && (!visited[rid] || !visited[bottomID]) {
- connSpec = dimSpec
- }
- grid[gr][gc] = mapCell{char: mg.connectorV, spec: connSpec}
- }
- }
- }
- }
-
- return renderMapCells(grid, colorMode, 0, mapHeight, 0)
-}
-
-func roomsVisited(sess *net.Session) map[int]bool {
- if sess != nil && sess.Player != nil {
- return sess.Player.Stats.RoomsVisited
- }
- return nil
-}
-
-func colorModeFor(sess *net.Session) string {
- if sess != nil && sess.Player != nil {
- return sess.Player.OptionString("color")
- }
- return "none"
-}
-
-func resolveMapAt(g *Game, sess *net.Session) color.ColorSpec {
- if sess != nil {
- return g.resolveColor(sess, "map_at")
- }
- return color.NoColor()
-}
-
-func resolveDim(g *Game, sess *net.Session) color.ColorSpec {
- if sess != nil {
- return g.resolveColor(sess, "dim")
- }
- return color.Parse("243 dim")
-}
-
-func roomMapSymbol(g *Game, sess *net.Session, roomID int, unvisited bool) (rune, color.ColorSpec) {
- if sess != nil && sess.Player != nil {
- if data, ok := sess.Player.MapSymbols[roomID]; ok {
- r, size := utf8.DecodeRuneInString(data.Char)
- if size > 0 && r != utf8.RuneError {
- spec := color.NoColor()
- if data.Color != "" {
- spec = color.Parse(data.Color)
- }
- // ponytail: non-ASCII custom symbols fall back to 'o'
- // when unicode mode is off, but preserve the color.
- if !sess.Player.OptionBool("unicode") && r > 127 {
- return 'o', spec
- }
- return r, spec
- }
- }
- if sess.Player.OptionBool("unicode") {
- if unvisited {
- return '□', color.NoColor()
- }
- return '■', color.NoColor()
- }
- return 'o', color.NoColor()
- }
- return '■', color.NoColor()
-}
-
-func exitsConnect(g *Game, room1, room2 int, dir12, dir21 world.ExitDir) bool {
- r1, ok := loadRoom(g, room1)
- if !ok {
- return false
- }
- if id, ok := exitTarget(r1, dir12); ok && id == room2 {
- return true
- }
- r2, ok := loadRoom(g, room2)
- if !ok {
- return false
- }
- id, ok := exitTarget(r2, dir21)
- return ok && id == room1
-}
-
-func exitTarget(room *world.Room, dir world.ExitDir) (int, bool) {
- if room == nil {
- return 0, false
- }
- exit, ok := room.Exits[dir]
- if !ok {
- return 0, false
- }
- return exit.Room, true
-}
-
-func loadRoom(g *Game, roomID int) (*world.Room, bool) {
- if roomID == 0 {
- return nil, false
- }
- room, err := g.World.LoadRoom(roomID)
- if err != nil {
- return nil, false
- }
- return room, true
-}
-
-func stripBlankRows(lines []string) []string {
- var out []string
- for _, line := range lines {
- if strings.TrimSpace(line) != "" {
- out = append(out, line)
- }
- }
- return out
-}
-
-func leftTrimCommon(lines []string) []string {
- min := -1
- for _, line := range lines {
- if strings.TrimSpace(line) == "" {
- continue
- }
- n := 0
- for _, r := range line {
- if r == ' ' {
- n++
- } else {
- break
- }
- }
- if min < 0 || n < min {
- min = n
- }
- }
- if min <= 0 {
- return lines
- }
- result := make([]string, len(lines))
- for i, line := range lines {
- if len(line) <= min {
- result[i] = ""
- } else {
- result[i] = line[min:]
- }
- }
- return result
-}