aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/api_rooms.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-30 02:17:22 -0400
committerhistoria <[not public]>2026-06-30 02:17:22 -0400
commit7a4b91304061777e7b3365b505d1e74878043d35 (patch)
tree4440b95c6ed4425f19919a4e74890607d008feec /internal/admin/api_rooms.go
parent069d3c1c81d71042706372df153254487a765dfc (diff)
downloadthehouseoficarus-7a4b91304061777e7b3365b505d1e74878043d35.tar.gz
feat: web gui mapping and obj/item/mob placement improvements
Diffstat (limited to 'internal/admin/api_rooms.go')
-rw-r--r--internal/admin/api_rooms.go192
1 files changed, 147 insertions, 45 deletions
diff --git a/internal/admin/api_rooms.go b/internal/admin/api_rooms.go
index 985eb49..4075534 100644
--- a/internal/admin/api_rooms.go
+++ b/internal/admin/api_rooms.go
@@ -269,68 +269,164 @@ func (s *AdminServer) handleRoomDirs(w http.ResponseWriter, r *http.Request) {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
- entries, err := os.ReadDir(filepath.Join(s.dataDir, "rooms"))
+ base := filepath.Join(s.dataDir, "rooms")
+ entries, err := os.ReadDir(base)
if err != nil {
writeJSON(w, []string{""})
return
}
dirs := []string{""}
for _, e := range entries {
- if e.IsDir() {
+ if !e.IsDir() {
+ continue
+ }
+ if hasYAMLFiles(filepath.Join(base, e.Name())) {
dirs = append(dirs, e.Name())
}
+ subEntries, err := os.ReadDir(filepath.Join(base, e.Name()))
+ if err != nil {
+ continue
+ }
+ for _, se := range subEntries {
+ if se.IsDir() && hasYAMLFiles(filepath.Join(base, e.Name(), se.Name())) {
+ dirs = append(dirs, e.Name()+"/"+se.Name())
+ }
+ }
}
writeJSON(w, dirs)
}
+func hasYAMLFiles(dir string) bool {
+ entries, err := os.ReadDir(dir)
+ if err != nil {
+ return false
+ }
+ for _, e := range entries {
+ if !e.IsDir() && filepath.Ext(e.Name()) == ".yaml" {
+ return true
+ }
+ }
+ return false
+}
+
func (s *AdminServer) handleRoomLink(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodPost:
var body struct {
- From int `json:"from"`
- To int `json:"to"`
+ From int `json:"from"`
+ To int `json:"to"`
+ Dir string `json:"dir"`
}
if err := readJSON(r, &body); err != nil || body.From <= 0 || body.To <= 0 {
writeJSON(w, map[string]any{"error": "invalid body, need from and to"})
return
}
- grid := world.BuildGrid(s.cfg.StartingRoom, func(id int) (*world.Room, bool) {
- room, err := s.world.LoadRoom(id)
- if err != nil {
- return nil, false
- }
- return room, true
- }, nil, nil)
- cA, okA := grid.Coord[body.From]
- cB, okB := grid.Coord[body.To]
- if !okA || !okB || cA[2] != cB[2] {
- writeJSON(w, map[string]any{"error": "rooms must be on same z-level"})
+ roomA, errA := s.world.LoadRoom(body.From)
+ roomB, errB := s.world.LoadRoom(body.To)
+ if errA != nil || errB != nil {
+ writeJSON(w, map[string]any{"error": "one or both rooms not found"})
return
}
- dx, dy := cB[0]-cA[0], cB[1]-cA[1]
+
var dir, oppDir world.ExitDir
- for d, delta := range world.DirectionDeltas3D {
- if delta[0] == dx && delta[1] == dy && delta[2] == 0 {
- dir = d
- oppDir = world.OppositeExit[d]
- break
+ if body.Dir != "" {
+ dir = world.ExitDir(strings.ToLower(body.Dir))
+ if opp, ok := world.OppositeExit[dir]; ok {
+ oppDir = opp
+ } else {
+ writeJSON(w, map[string]any{"error": "invalid direction: " + body.Dir})
+ return
+ }
+ } else {
+ grid := world.BuildGrid(s.cfg.StartingRoom, func(id int) (*world.Room, bool) {
+ room, err := s.world.LoadRoom(id)
+ if err != nil {
+ return nil, false
+ }
+ return room, true
+ }, nil, nil)
+
+ cA, okA := grid.Coord[body.From]
+ cB, okB := grid.Coord[body.To]
+ if !okA || !okB || cA[2] != cB[2] {
+ writeJSON(w, map[string]any{"error": "rooms must be on same z-level"})
+ return
+ }
+ dx, dy := cB[0]-cA[0], cB[1]-cA[1]
+ for d, delta := range world.DirectionDeltas3D {
+ if delta[0] == dx && delta[1] == dy && delta[2] == 0 {
+ dir = d
+ oppDir = world.OppositeExit[d]
+ break
+ }
+ }
+ if dir == "" {
+ writeJSON(w, map[string]any{"error": "rooms are not adjacent"})
+ return
+ }
+ }
+
+ if roomA.Exits != nil {
+ for d, exit := range roomA.Exits {
+ if exit.Room == body.To {
+ delete(roomA.Exits, d)
+ break
+ }
+ }
+ }
+ if roomB.Exits != nil {
+ for d, exit := range roomB.Exits {
+ if exit.Room == body.From {
+ delete(roomB.Exits, d)
+ break
+ }
}
}
- if dir == "" {
- writeJSON(w, map[string]any{"error": "rooms are not adjacent"})
+
+ conflicts := 0
+ testGrid := world.BuildGrid(s.cfg.StartingRoom, func(id int) (*world.Room, bool) {
+ room, err := s.world.LoadRoom(id)
+ if err != nil {
+ return nil, false
+ }
+ roomCopy := *room
+ if room.Exits != nil {
+ roomCopy.Exits = make(map[world.ExitDir]world.ExitDef, len(room.Exits))
+ for k, v := range room.Exits {
+ if (id == body.From && v.Room == body.To) || (id == body.To && v.Room == body.From) {
+ continue
+ }
+ roomCopy.Exits[k] = v
+ }
+ } else {
+ roomCopy.Exits = make(map[world.ExitDir]world.ExitDef)
+ }
+ if id == body.From {
+ roomCopy.Exits[dir] = world.ExitDef{Room: body.To}
+ }
+ if id == body.To {
+ roomCopy.Exits[oppDir] = world.ExitDef{Room: body.From}
+ }
+ return &roomCopy, true
+ }, nil, func(gc world.GridConflict) {
+ conflicts++
+ })
+ _ = testGrid
+
+ if conflicts > 0 {
+ writeJSONError(w, "link would cause map conflicts (twists or overlaps)", http.StatusConflict)
return
}
- roomA, _ := s.world.LoadRoom(body.From)
- roomB, _ := s.world.LoadRoom(body.To)
if roomA.Exits == nil {
roomA.Exits = make(map[world.ExitDir]world.ExitDef)
}
+ roomA.Exits[dir] = world.ExitDef{Room: body.To}
+
if roomB.Exits == nil {
roomB.Exits = make(map[world.ExitDir]world.ExitDef)
}
- roomA.Exits[dir] = world.ExitDef{Room: body.To}
roomB.Exits[oppDir] = world.ExitDef{Room: body.From}
pathA, okA2 := s.world.GetRoomPath(body.From)
@@ -378,36 +474,42 @@ func (s *AdminServer) handleRoomLink(w http.ResponseWriter, r *http.Request) {
writeJSON(w, map[string]any{"error": "invalid body"})
return
}
- grid := world.BuildGrid(s.cfg.StartingRoom, func(id int) (*world.Room, bool) {
- room, err := s.world.LoadRoom(id)
- if err != nil {
- return nil, false
- }
- return room, true
- }, nil, nil)
- cA, okA := grid.Coord[body.From]
- cB, okB := grid.Coord[body.To]
- if !okA || !okB || cA[2] != cB[2] {
- writeJSON(w, map[string]any{"error": "rooms must be on same z-level"})
+ roomA, errA := s.world.LoadRoom(body.From)
+ roomB, errB := s.world.LoadRoom(body.To)
+ if errA != nil || errB != nil {
+ writeJSON(w, map[string]any{"error": "one or both rooms not found"})
return
}
- dx, dy := cB[0]-cA[0], cB[1]-cA[1]
+ if roomA == nil || roomB == nil {
+ writeJSON(w, map[string]any{"error": "one or both rooms not found"})
+ return
+ }
+
var dir, oppDir world.ExitDir
- for d, delta := range world.DirectionDeltas3D {
- if delta[0] == dx && delta[1] == dy && delta[2] == 0 {
+ for _, d := range world.ExitOrder {
+ if exit, ok := roomA.Exits[d]; ok && exit.Room == body.To {
dir = d
- oppDir = world.OppositeExit[d]
break
}
}
- if dir == "" {
- writeJSON(w, map[string]any{"error": "rooms are not adjacent"})
+ if dir != "" {
+ oppDir = world.OppositeExit[dir]
+ } else {
+ for _, d := range world.ExitOrder {
+ if exit, ok := roomB.Exits[d]; ok && exit.Room == body.From {
+ oppDir = d
+ dir = world.OppositeExit[d]
+ break
+ }
+ }
+ }
+
+ if dir == "" && oppDir == "" {
+ writeJSONError(w, "no exit found between these rooms", http.StatusNotFound)
return
}
- roomA, _ := s.world.LoadRoom(body.From)
- roomB, _ := s.world.LoadRoom(body.To)
if roomA.Exits != nil {
delete(roomA.Exits, dir)
}