diff options
| -rw-r--r-- | data/.admin_history.json | 2 | ||||
| -rw-r--r-- | data/rooms/intro/2001.yaml | 2 | ||||
| -rw-r--r-- | data/rooms/intro/2006.yaml | 14 | ||||
| -rw-r--r-- | internal/admin/api_map.go | 19 | ||||
| -rw-r--r-- | internal/admin/static/map.js | 77 | ||||
| -rw-r--r-- | internal/admin/templates/map.html | 2 |
6 files changed, 93 insertions, 23 deletions
diff --git a/data/.admin_history.json b/data/.admin_history.json index caadf98..7198539 100644 --- a/data/.admin_history.json +++ b/data/.admin_history.json @@ -1 +1 @@ -{"history":[{"time":"2026-07-09T16:59:13-04:00","description":"create room 1006","file_path":"data/rooms/intro/1006.yaml","old_content":"","new_content":"exits:\n west:\n room: 1005\nid: 1006\nname: 'Room #1006'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1005.yaml","old_content":"exits:\n west:\n room: 2001\nid: 1005\nname: 'Room #1005'\n","new_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T16:59:13-04:00","description":"create room 1007","file_path":"data/rooms/intro/1007.yaml","old_content":"","new_content":"exits:\n south:\n room: 1006\nid: 1007\nname: 'Room #1007'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1006.yaml","old_content":"exits:\n west:\n room: 1005\nid: 1006\nname: 'Room #1006'\n","new_content":"id: 1006\nname: 'Room #1006'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1007\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T16:59:14-04:00","description":"create room 1008","file_path":"data/rooms/intro/1008.yaml","old_content":"","new_content":"exits:\n east:\n room: 1007\nid: 1008\nname: 'Room #1008'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1007.yaml","old_content":"exits:\n south:\n room: 1006\nid: 1007\nname: 'Room #1007'\n","new_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1006\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T16:59:14-04:00","description":"link 1008 south 1005","file_path":"data/rooms/intro/1008.yaml","old_content":"exits:\n east:\n room: 1007\nid: 1008\nname: 'Room #1008'\n","new_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1007\n south:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-09T16:59:14-04:00","description":"link 1005 north 1008 (reverse)","file_path":"data/rooms/intro/1005.yaml","old_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n north:\n room: 1008\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-09T16:59:15-04:00","description":"create room 1009","file_path":"data/rooms/intro/1009.yaml","old_content":"","new_content":"exits:\n south:\n room: 1008\nid: 1009\nname: 'Room #1009'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1008.yaml","old_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1007\n south:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1007\n north:\n room: 1009\n south:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T16:59:15-04:00","description":"create room 1010","file_path":"data/rooms/intro/1010.yaml","old_content":"","new_content":"exits:\n west:\n room: 1009\nid: 1010\nname: 'Room #1010'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1009.yaml","old_content":"exits:\n south:\n room: 1008\nid: 1009\nname: 'Room #1009'\n","new_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1010\n south:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T16:59:16-04:00","description":"link 1010 south 1007","file_path":"data/rooms/intro/1010.yaml","old_content":"exits:\n west:\n room: 1009\nid: 1010\nname: 'Room #1010'\n","new_content":"id: 1010\nname: 'Room #1010'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1007\n west:\n room: 1009\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-09T16:59:16-04:00","description":"link 1007 north 1010 (reverse)","file_path":"data/rooms/intro/1007.yaml","old_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1006\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1010\n south:\n room: 1006\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-09T16:59:34-04:00","description":"delete 4 rooms (cleaned 2 exits)","file_path":"data/rooms/intro/1009.yaml","old_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1010\n south:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false,"extra_files":[{"file_path":"data/rooms/intro/1005.yaml","old_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n north:\n room: 1008\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"},{"file_path":"data/rooms/intro/1006.yaml","old_content":"id: 1006\nname: 'Room #1006'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1007\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1006\nname: 'Room #1006'\ncolor: \"\"\ndescription: []\nexits:\n west:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"},{"file_path":"data/rooms/intro/1008.yaml","old_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1007\n north:\n room: 1009\n south:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true},{"file_path":"data/rooms/intro/1010.yaml","old_content":"id: 1010\nname: 'Room #1010'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1007\n west:\n room: 1009\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true},{"file_path":"data/rooms/intro/1007.yaml","old_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1010\n south:\n room: 1006\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true}]},{"time":"2026-07-09T17:09:03-04:00","description":"create room 1007","file_path":"data/rooms/intro/1007.yaml","old_content":"","new_content":"exits:\n south:\n room: 1005\nid: 1007\nname: 'Room #1007'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1005.yaml","old_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n north:\n room: 1007\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T17:09:03-04:00","description":"create room 1008","file_path":"data/rooms/intro/1008.yaml","old_content":"","new_content":"exits:\n south:\n room: 1007\nid: 1008\nname: 'Room #1008'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1007.yaml","old_content":"exits:\n south:\n room: 1005\nid: 1007\nname: 'Room #1007'\n","new_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1008\n south:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T17:09:04-04:00","description":"create room 1009","file_path":"data/rooms/intro/1009.yaml","old_content":"","new_content":"exits:\n west:\n room: 1008\nid: 1009\nname: 'Room #1009'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1008.yaml","old_content":"exits:\n south:\n room: 1007\nid: 1008\nname: 'Room #1008'\n","new_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1009\n south:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T17:09:04-04:00","description":"create room 1010","file_path":"data/rooms/intro/1010.yaml","old_content":"","new_content":"exits:\n north:\n room: 1009\nid: 1010\nname: 'Room #1010'\n","is_delete":false,"is_create":true,"extra_files":[{"file_path":"data/rooms/intro/1009.yaml","old_content":"exits:\n west:\n room: 1008\nid: 1009\nname: 'Room #1009'\n","new_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1010\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"}]},{"time":"2026-07-09T17:09:04-04:00","description":"link 1010 west 1007","file_path":"data/rooms/intro/1010.yaml","old_content":"exits:\n north:\n room: 1009\nid: 1010\nname: 'Room #1010'\n","new_content":"id: 1010\nname: 'Room #1010'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1009\n west:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:09:04-04:00","description":"link 1007 east 1010 (reverse)","file_path":"data/rooms/intro/1007.yaml","old_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1008\n south:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1010\n north:\n room: 1008\n south:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:09:09-04:00","description":"attach room 1007 to course testcourse","file_path":"data/courses/testcourse.yaml","old_content":"completion_xp: 0\nname: Test Course\nobstacles:\n - exit_dir: east\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 2005\n verb: climb\n xp: 0\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n room_id: 2004\n verb: climb\n xp: 0\n - exit_dir: west\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 2003\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 2002\n","new_content":"completion_xp: 0\nname: Test Course\nobstacles:\n - exit_dir: east\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 2005\n verb: climb\n xp: 0\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n room_id: 2004\n verb: climb\n xp: 0\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 2003\n verb: climb\n xp: 0\n - fail_damage:\n - 0\n - 0\n room_id: 1007\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 2002\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:09:51-04:00","description":"Created course test","file_path":"data/courses/test.yaml","old_content":"","new_content":"completion_xp: 0\nname: test\nobstacles:\n - fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","is_delete":false,"is_create":true},{"time":"2026-07-09T17:09:57-04:00","description":"Updated course test","file_path":"data/courses/test.yaml","old_content":"completion_xp: 0\nname: test\nobstacles:\n - fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","new_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:09:57-04:00","description":"attach room 1008 to course test","file_path":"data/courses/test.yaml","old_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","new_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - fail_damage:\n - 0\n - 0\n room_id: 1008\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:09:59-04:00","description":"attach room 1009 to course test","file_path":"data/courses/test.yaml","old_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - fail_damage:\n - 0\n - 0\n room_id: 1008\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","new_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - exit_dir: east\n fail_damage:\n - 0\n - 0\n room_id: 1008\n verb: climb\n xp: 0\n - fail_damage:\n - 0\n - 0\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:10:01-04:00","description":"Updated course test","file_path":"data/courses/test.yaml","old_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - exit_dir: east\n fail_damage:\n - 0\n - 0\n room_id: 1008\n verb: climb\n xp: 0\n - fail_damage:\n - 0\n - 0\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","new_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - exit_dir: east\n fail_damage:\n - 0\n - 0\n room_id: 1008\n verb: climb\n xp: 0\n - exit_dir: southwest\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:10:01-04:00","description":"link 1009 southwest 1007","file_path":"data/rooms/intro/1009.yaml","old_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1010\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1010\n southwest:\n room: 1007\n hidden: true\n always_blocked: true\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:10:01-04:00","description":"Updated course test","file_path":"data/courses/test.yaml","old_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - exit_dir: east\n fail_damage:\n - 0\n - 0\n room_id: 1008\n verb: climb\n xp: 0\n - exit_dir: southwest\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","new_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - exit_dir: east\n fail_damage:\n - 0\n - 0\n room_id: 1008\n verb: climb\n xp: 0\n - exit_dir: southwest\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:10:07-04:00","description":"detach room 1008 from course test","file_path":"data/courses/test.yaml","old_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: north\n fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - exit_dir: east\n fail_damage:\n - 0\n - 0\n room_id: 1008\n verb: climb\n xp: 0\n - exit_dir: southwest\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","new_content":"completion_xp: 0\nname: test\nobstacles:\n - fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - exit_dir: southwest\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:10:07-04:00","description":"detach room 1007 from course test","file_path":"data/courses/test.yaml","old_content":"completion_xp: 0\nname: test\nobstacles:\n - fail_damage:\n - 0\n - 0\n phases:\n - delay: 0\n message: \"\"\n room_id: 1007\n verb: climb\n xp: 0\n - exit_dir: southwest\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","new_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: southwest\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","is_delete":false,"is_create":false}],"redo":[{"time":"2026-07-09T17:10:07-04:00","description":"delete 4 rooms (cleaned 1 exits)","file_path":"data/rooms/intro/1008.yaml","old_content":"id: 1008\nname: 'Room #1008'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1009\n south:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true,"is_create":false,"extra_files":[{"file_path":"data/rooms/intro/1005.yaml","old_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n north:\n room: 1007\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 1005\nname: 'Room #1005'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1006\n west:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n"},{"file_path":"data/rooms/intro/1007.yaml","old_content":"id: 1007\nname: 'Room #1007'\ncolor: \"\"\ndescription: []\nexits:\n east:\n room: 1010\n north:\n room: 1008\n south:\n room: 1005\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true},{"file_path":"data/rooms/intro/1009.yaml","old_content":"id: 1009\nname: 'Room #1009'\ncolor: \"\"\ndescription: []\nexits:\n south:\n room: 1010\n southwest:\n room: 1007\n hidden: true\n always_blocked: true\n west:\n room: 1008\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true},{"file_path":"data/rooms/intro/1010.yaml","old_content":"id: 1010\nname: 'Room #1010'\ncolor: \"\"\ndescription: []\nexits:\n north:\n room: 1009\n west:\n room: 1007\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"","is_delete":true}]},{"time":"2026-07-09T17:10:07-04:00","description":"delete course test (last obstacle removed)","file_path":"data/courses/test.yaml","old_content":"completion_xp: 0\nname: test\nobstacles:\n - exit_dir: southwest\n fail_damage:\n - 0\n - 0\n phases: []\n room_id: 1009\n verb: climb\n xp: 0\nrequired_level: 1\nstart_room: 1007\n","new_content":"","is_delete":true,"is_create":false}]}
\ No newline at end of file +{"history":[{"time":"2026-07-09T17:47:14-04:00","description":"unlink 2006 east 2005","file_path":"data/rooms/intro/2006.yaml","old_content":"block_transport: false\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 2005\n north:\n room: 2001\nname: Test Room\n","new_content":"id: 2006\nname: Test Room\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n north:\n room: 2001\nobjects: []\nitem_spawns: []\nmobs: []\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false},{"time":"2026-07-09T17:47:15-04:00","description":"unlink 2001 southeast 2005","file_path":"data/rooms/intro/2001.yaml","old_content":"id: 2001\nname: Test Room\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 1005\n south:\n room: 2006\n southeast:\n room: 2005\n west:\n room: 1001\nobjects:\n - name: hidden guy\n hidden: true\n description:\n - text: YOU FOUND HIM\n - id: tree\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 1\n - id: bucket_of_water\n quantity: 1\n respawn_ticks: 1\nmobs:\n - id: man\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","new_content":"id: 2001\nname: Test Room\ncolor: \"\"\ndescription:\n - text: It's a test room\nexits:\n east:\n room: 1005\n south:\n room: 2006\n west:\n room: 1001\nobjects:\n - name: hidden guy\n hidden: true\n description:\n - text: YOU FOUND HIM\n - id: tree\nitem_spawns:\n - id: bronze_axe\n quantity: 1\n respawn_ticks: 1\n - id: pot_of_flour\n quantity: 1\n respawn_ticks: 1\n - id: bucket_of_water\n quantity: 1\n respawn_ticks: 1\nmobs:\n - id: man\non_enter: []\nhazard: \"\"\nblock_transport: false\ntriggers: []\n","is_delete":false,"is_create":false}],"redo":null}
\ No newline at end of file diff --git a/data/rooms/intro/2001.yaml b/data/rooms/intro/2001.yaml index 2cfcd83..8f1db38 100644 --- a/data/rooms/intro/2001.yaml +++ b/data/rooms/intro/2001.yaml @@ -8,8 +8,6 @@ exits: room: 1005 south: room: 2006 - southeast: - room: 2005 west: room: 1001 objects: diff --git a/data/rooms/intro/2006.yaml b/data/rooms/intro/2006.yaml index 993216c..5b3019f 100644 --- a/data/rooms/intro/2006.yaml +++ b/data/rooms/intro/2006.yaml @@ -1,9 +1,15 @@ -block_transport: false +id: 2006 +name: Test Room +color: "" description: - text: It's a test room exits: - east: - room: 2005 north: room: 2001 -name: Test Room +objects: [] +item_spawns: [] +mobs: [] +on_enter: [] +hazard: "" +block_transport: false +triggers: [] diff --git a/internal/admin/api_map.go b/internal/admin/api_map.go index f5fd9ee..6e62d9e 100644 --- a/internal/admin/api_map.go +++ b/internal/admin/api_map.go @@ -175,6 +175,7 @@ func (s *AdminServer) handleMap(w http.ResponseWriter, r *http.Request) { Bidirectional bool `json:"bidirectional"` Hidden bool `json:"hidden"` AlwaysBlocked bool `json:"always_blocked"` + Missing bool `json:"missing"` } type UDLink struct { @@ -243,8 +244,22 @@ func (s *AdminServer) handleMap(w http.ResponseWriter, r *http.Request) { if target <= 0 { continue } - targetCoord, ok := g.Coord[target] - if !ok { + targetCoord, hasCoord := g.Coord[target] + targetExists := s.world.RoomIndex()[target] + + if !targetExists || !hasCoord { + if dir != world.Up && dir != world.Down { + key := linkKey(rid, target) + if !seenLinks[key] { + seenLinks[key] = true + links = append(links, LinkEntry{ + From: rid, + To: target, + Dir: string(dir), + Missing: true, + }) + } + } continue } diff --git a/internal/admin/static/map.js b/internal/admin/static/map.js index e053dda..00230a4 100644 --- a/internal/admin/static/map.js +++ b/internal/admin/static/map.js @@ -277,8 +277,21 @@ function renderMap(data) { if (data.links) { data.links.forEach(function(l) { var fr = roomMap[l.from], tr = roomMap[l.to]; - if (!fr || !tr) return; + if (!fr) return; var x1 = fr.x * CELL + CELL/2, y1 = fr.y * CELL + CELL/2; + if (!tr) { + if (l.missing) { + var dd = gridDirs.find(function(d) { return d.dir === l.dir; }); + if (dd) { + var dx = (fr.x + dd.dx) * CELL + CELL/2; + var dy = (fr.y + dd.dy) * CELL + CELL/2; + g += '<line x1="'+x1+'" y1="'+y1+'" x2="'+dx+'" y2="'+dy+'" stroke="#f80" stroke-width="3" opacity="0.6" stroke-dasharray="6,4" vector-effect="non-scaling-stroke"/>'; + g += '<text x="'+dx+'" y="'+(dy+5)+'" text-anchor="middle" font-size="14" fill="#f80" pointer-events="none" font-family="monospace">?</text>'; + g += '<text x="'+dx+'" y="'+(dy+20)+'" text-anchor="middle" font-size="8" fill="#f80" opacity="0.8" pointer-events="none" font-family="monospace">#'+l.to+'</text>'; + } + } + return; + } var x2 = tr.x * CELL + CELL/2, y2 = tr.y * CELL + CELL/2; if (l.always_blocked) { g += '<line x1="'+x1+'" y1="'+y1+'" x2="'+x2+'" y2="'+y2+'" stroke="#C44" stroke-width="5" opacity="0.75" stroke-dasharray="6,3" vector-effect="non-scaling-stroke"/>'; @@ -2683,32 +2696,70 @@ function updateDelDragTarget(e) { var gp = mouseToGrid(e); if (!gp) return; - var neighbor = occupied[gp.x+','+gp.y]; - if (!neighbor || neighbor === delFromId) return; - var gdx = gp.x - fromRoom.x; var gdy = gp.y - fromRoom.y; if (Math.abs(gdx) > 1 || Math.abs(gdy) > 1) return; if (gdx === 0 && gdy === 0) return; - if (!gridDeltaToDir(gdx, gdy)) return; + var dir = gridDeltaToDir(gdx, gdy); + if (!dir) return; - delTargetId = neighbor; - applyDelHighlight(delFromId, delTargetId); + var neighbor = occupied[gp.x+','+gp.y]; + if (neighbor && neighbor !== delFromId) { + delTargetId = neighbor; + applyDelHighlight(delFromId, delTargetId); + return; + } + + if (mapData && mapData.links) { + for (var i = 0; i < mapData.links.length; i++) { + var lk = mapData.links[i]; + if (lk.from === delFromId && lk.missing && lk.dir === dir) { + delTargetId = lk.to; + applyDelHighlight(delFromId, delTargetId); + return; + } + } + } } function applyDelHighlight(fromId, toId) { clearDelHighlight(); var fromEl = document.querySelector('.rm[data-id="'+fromId+'"]'); - var toEl = document.querySelector('.rm[data-id="'+toId+'"]'); var fromRoom = roomMap[fromId]; + if (!fromEl || !fromRoom) return; + + var toEl = document.querySelector('.rm[data-id="'+toId+'"]'); var toRoom = roomMap[toId]; - if (!fromEl || !toEl || !fromRoom || !toRoom) return; + var tx, ty; + + if (!toRoom) { + var found = false; + if (mapData && mapData.links) { + for (var i = 0; i < mapData.links.length; i++) { + var l = mapData.links[i]; + if (l.from === fromId && l.to === toId && l.missing) { + var dd = gridDirs.find(function(d) { return d.dir === l.dir; }); + if (dd) { + tx = (fromRoom.x + dd.dx) * CELL + CELL/2; + ty = (fromRoom.y + dd.dy) * CELL + CELL/2; + found = true; + } + break; + } + } + } + if (!found) return; + } else { + if (!toEl) return; + tx = toRoom.x * CELL + CELL/2; + ty = toRoom.y * CELL + CELL/2; + toEl.setAttribute('stroke', '#f55'); + toEl.setAttribute('stroke-width', '3'); + } fromEl.setAttribute('stroke', '#f55'); fromEl.setAttribute('stroke-width', '3'); - toEl.setAttribute('stroke', '#f55'); - toEl.setAttribute('stroke-width', '3'); var g = document.querySelector('#mapSvg g'); if (!g) return; @@ -2717,8 +2768,8 @@ function applyDelHighlight(fromId, toId) { line.id = '__dl_line'; line.setAttribute('x1', fromRoom.x * CELL + CELL/2); line.setAttribute('y1', fromRoom.y * CELL + CELL/2); - line.setAttribute('x2', toRoom.x * CELL + CELL/2); - line.setAttribute('y2', toRoom.y * CELL + CELL/2); + line.setAttribute('x2', tx); + line.setAttribute('y2', ty); line.setAttribute('stroke', '#f55'); line.setAttribute('stroke-width', '3'); line.setAttribute('stroke-dasharray', '6,3'); diff --git a/internal/admin/templates/map.html b/internal/admin/templates/map.html index eb95db7..0915aaa 100644 --- a/internal/admin/templates/map.html +++ b/internal/admin/templates/map.html @@ -1,6 +1,6 @@ {{define "body-map"}} <div class="layout"> - <div class="panel panel-left" id="disconnectedPanel"> + <div class="panel panel-left" id="disconnectedPanel" style="display:none"> <div class="panel-header">Disconnected Rooms</div> <div id="disconnectedList" style="overflow-y:auto;flex:1"> <p style="color:#888;text-align:center;margin-top:20px;font-size:11px">None</p> |
