aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/api_map.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/admin/api_map.go')
-rw-r--r--internal/admin/api_map.go241
1 files changed, 241 insertions, 0 deletions
diff --git a/internal/admin/api_map.go b/internal/admin/api_map.go
new file mode 100644
index 0000000..1520279
--- /dev/null
+++ b/internal/admin/api_map.go
@@ -0,0 +1,241 @@
+package admin
+
+import (
+ "net/http"
+ "os"
+ "path/filepath"
+ "strconv"
+
+ "thehouseoficarus/internal/world"
+)
+
+func (s *AdminServer) handleMap(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed)
+ return
+ }
+
+ zStr := r.URL.Query().Get("z")
+ z, err := strconv.Atoi(zStr)
+ if err != nil {
+ z = 0
+ }
+
+ dir := r.URL.Query().Get("dir")
+ seed := s.cfg.StartingRoom
+ if dir != "" {
+ base := filepath.Join(s.dataDir, "rooms", dir)
+ if low, ok := findLowestRoom(base); ok {
+ seed = low
+ }
+ }
+
+ g := world.BuildGrid(seed, func(id int) (*world.Room, bool) {
+ room, err := s.world.LoadRoom(id)
+ if err != nil {
+ return nil, false
+ }
+ return room, true
+ }, nil, nil)
+
+ type RoomEntry struct {
+ ID int `json:"id"`
+ X int `json:"x"`
+ Y int `json:"y"`
+ Name string `json:"name"`
+ Color string `json:"color"`
+ Symbol string `json:"symbol"`
+ }
+
+ type LinkEntry struct {
+ From int `json:"from"`
+ To int `json:"to"`
+ Dir string `json:"dir"`
+ Bidirectional bool `json:"bidirectional"`
+ }
+
+ type UDLink struct {
+ From int `json:"from"`
+ To int `json:"to"`
+ }
+
+ roomData := make(map[int]*world.Room)
+ var rooms []RoomEntry
+
+ minX, maxX := 0, 0
+ minY, maxY := 0, 0
+ first := true
+
+ for rid, coord := range g.Coord {
+ if coord[2] != z {
+ continue
+ }
+
+ r, err := s.world.LoadRoom(rid)
+ if err != nil {
+ continue
+ }
+ roomData[rid] = r
+
+ rooms = append(rooms, RoomEntry{
+ ID: rid,
+ X: coord[0],
+ Y: coord[1],
+ Name: r.Name,
+ Color: r.Color,
+ })
+
+ if first {
+ minX, maxX = coord[0], coord[0]
+ minY, maxY = coord[1], coord[1]
+ first = false
+ } else {
+ if coord[0] < minX {
+ minX = coord[0]
+ }
+ if coord[0] > maxX {
+ maxX = coord[0]
+ }
+ if coord[1] < minY {
+ minY = coord[1]
+ }
+ if coord[1] > maxY {
+ maxY = coord[1]
+ }
+ }
+ }
+
+ var links []LinkEntry
+ var upLinks []UDLink
+ var downLinks []UDLink
+
+ seenLinks := make(map[string]bool)
+ for rid, room := range roomData {
+ c := g.Coord[rid]
+ for dir, exit := range room.Exits {
+ target := exit.Room
+ if target <= 0 {
+ continue
+ }
+ targetCoord, ok := g.Coord[target]
+ if !ok {
+ continue
+ }
+
+ if targetCoord[2] > c[2] {
+ upLinks = append(upLinks, UDLink{From: rid, To: target})
+ continue
+ }
+ if targetCoord[2] < c[2] {
+ downLinks = append(downLinks, UDLink{From: rid, To: target})
+ continue
+ }
+
+ if dir == world.Up || dir == world.Down {
+ continue
+ }
+
+ if _, onZ := roomData[target]; !onZ {
+ continue
+ }
+
+ bidirectional := false
+ if targetRoom, ok := roomData[target]; ok {
+ if oppExit, ok := targetRoom.Exits[world.OppositeExit[dir]]; ok && oppExit.Room == rid {
+ bidirectional = true
+ }
+ }
+
+ key := linkKey(rid, target)
+ if bidirectional && seenLinks[key] {
+ continue
+ }
+ seenLinks[key] = true
+
+ links = append(links, LinkEntry{
+ From: rid,
+ To: target,
+ Dir: string(dir),
+ Bidirectional: bidirectional,
+ })
+ }
+ }
+
+ writeJSON(w, map[string]any{
+ "rooms": rooms,
+ "links": links,
+ "upLinks": upLinks,
+ "downLinks": downLinks,
+ "dir": getRoomDir(s, seed),
+ "bounds": map[string]int{
+ "minX": minX,
+ "maxX": maxX,
+ "minY": minY,
+ "maxY": maxY,
+ },
+ })
+}
+
+func getRoomDir(s *AdminServer, roomID int) string {
+ path, ok := s.world.GetRoomPath(roomID)
+ if !ok {
+ return ""
+ }
+ rel, err := filepath.Rel(filepath.Join(s.dataDir, "rooms"), filepath.Dir(path))
+ if err != nil || rel == "." {
+ return ""
+ }
+ return rel
+}
+
+func linkKey(a, b int) string {
+ if a < b {
+ return strconv.Itoa(a) + "-" + strconv.Itoa(b)
+ }
+ return strconv.Itoa(b) + "-" + strconv.Itoa(a)
+}
+
+func findLowestRoom(dir string) (int, bool) {
+ entries, err := os.ReadDir(dir)
+ if err != nil {
+ return 0, false
+ }
+ lowest := 0
+ found := false
+ for _, e := range entries {
+ if e.IsDir() {
+ continue
+ }
+ name := e.Name()
+ if filepath.Ext(name) != ".yaml" {
+ continue
+ }
+ id, err := strconv.Atoi(name[:len(name)-5])
+ if err != nil || id <= 0 {
+ continue
+ }
+ if !found || id < lowest {
+ lowest = id
+ found = true
+ }
+ }
+ return lowest, found
+}
+
+func (s *AdminServer) handleNextRoomID(w http.ResponseWriter, r *http.Request) {
+ if r.Method != http.MethodGet {
+ http.Error(w, `{"error":"method not allowed"}`, http.StatusMethodNotAllowed)
+ return
+ }
+ fromStr := r.URL.Query().Get("from")
+ fromID := 0
+ if fromStr != "" {
+ fromID, _ = strconv.Atoi(fromStr)
+ }
+ id, _, err := nextRoomIDInDir(s.dataDir, fromID)
+ if err != nil {
+ writeJSON(w, map[string]any{"error": err.Error()})
+ return
+ }
+ writeJSON(w, map[string]any{"id": id})
+}