From 018b265f3c4f2afe54492516f4a20c0461c4b7e9 Mon Sep 17 00:00:00 2001
From: Volte6 <143822+Volte6@users.noreply.github.com>
Date: Wed, 6 May 2026 00:22:01 -0700
Subject: [PATCH] fast travel module
---
.../world/default/rooms/frostfang/1067.yaml | 21 ++
.../world/default/rooms/frostfang/879.yaml | 6 +-
.../world/default/rooms/mystarion/844.yaml | 3 +
internal/mapper/mapper_test.go | 14 +-
modules/all-modules.go | 1 +
modules/fasttravel/fasttravel.go | 290 ++++++++++++++++++
.../files/data-overlays/config.yaml | 15 +
.../files/data-overlays/keywords.yaml | 6 +
.../html/admin/fasttravel-config.html | 238 ++++++++++++++
.../templates/help/fasttravel.template | 13 +
10 files changed, 596 insertions(+), 11 deletions(-)
create mode 100755 _datafiles/world/default/rooms/frostfang/1067.yaml
create mode 100644 modules/fasttravel/fasttravel.go
create mode 100644 modules/fasttravel/files/data-overlays/config.yaml
create mode 100644 modules/fasttravel/files/data-overlays/keywords.yaml
create mode 100644 modules/fasttravel/files/datafiles/html/admin/fasttravel-config.html
create mode 100644 modules/fasttravel/files/datafiles/templates/help/fasttravel.template
diff --git a/_datafiles/world/default/rooms/frostfang/1067.yaml b/_datafiles/world/default/rooms/frostfang/1067.yaml
new file mode 100755
index 000000000..2903e1d48
--- /dev/null
+++ b/_datafiles/world/default/rooms/frostfang/1067.yaml
@@ -0,0 +1,21 @@
+roomid: 1067
+zone: Frostfang
+title: Magic Academy Back Room
+description: The dusty back room of the Magic Academy is seldom visited, its air thick
+ with the quiet weight of forgotten knowledge and the faint scent of old parchment.
+ Shelves sag beneath the burden of neglected tomes and curious relics, their secrets
+ long undisturbed. At the heart of the chamber, a glowing dais hums with a steady,
+ almost breathing rhythm, its light spilling softly across the worn stone floor in
+ rippling patterns. Arcane sigils flicker and coil along its surface like living
+ things, whispering of distant places and unseen pathways. The very air around it
+ shimmers, charged with restrained energy.
+biome: city
+exits:
+ southeast:
+ roomid: 879
+tags:
+- fast travel
+mapx: -3
+mapy: -6
+mapz: 0
+hascoordinates: true
diff --git a/_datafiles/world/default/rooms/frostfang/879.yaml b/_datafiles/world/default/rooms/frostfang/879.yaml
index 8e44b1cea..60dd729bd 100755
--- a/_datafiles/world/default/rooms/frostfang/879.yaml
+++ b/_datafiles/world/default/rooms/frostfang/879.yaml
@@ -13,14 +13,12 @@ maplegend: Trainer
exits:
east:
roomid: 5
+ northwest:
+ roomid: 1067
spawninfo:
- mobid: 50
message: A mage enters the room.
respawnrate: 2 real minutes
-skilltraining:
- cast:
- min: 1
- max: 4
mapx: -1
mapy: -4
mapz: 0
diff --git a/_datafiles/world/default/rooms/mystarion/844.yaml b/_datafiles/world/default/rooms/mystarion/844.yaml
index 1ebd90d41..0df86d1cd 100755
--- a/_datafiles/world/default/rooms/mystarion/844.yaml
+++ b/_datafiles/world/default/rooms/mystarion/844.yaml
@@ -10,9 +10,12 @@ description: At the very end of Bloodroot Way, the shadows converge, and the air
Shelves are lined with ancient, leather-bound tomes etched with glowing runes, alongside
vials of mysterious, shimmering elixirs. Enchanted relics float in eerie stillness,
their soft glow casting an otherworldly light.
+biome: city
exits:
south:
roomid: 843
+tags:
+- fast travel
mapx: 71
mapy: -4
mapz: 0
diff --git a/internal/mapper/mapper_test.go b/internal/mapper/mapper_test.go
index 020e59e16..19ce84d22 100644
--- a/internal/mapper/mapper_test.go
+++ b/internal/mapper/mapper_test.go
@@ -52,13 +52,13 @@ func TestArrowForDelta(t *testing.T) {
dx, dy, dz int
want rune
}{
- {0, -1, 0, '\u2502'}, // north: vertical bar
- {0, 1, 0, '\u2502'}, // south: vertical bar
- {-1, 0, 0, '\u2500'}, // west: horizontal bar
- {1, 0, 0, '\u2500'}, // east: horizontal bar
- {1, -1, 0, '\u2571'}, // northeast: slash
- {-1, 1, 0, '\u2571'}, // southwest: slash
- {1, 1, 0, '\u2572'}, // southeast: backslash
+ {0, -1, 0, '\u2502'}, // north: vertical bar
+ {0, 1, 0, '\u2502'}, // south: vertical bar
+ {-1, 0, 0, '\u2500'}, // west: horizontal bar
+ {1, 0, 0, '\u2500'}, // east: horizontal bar
+ {1, -1, 0, '\u2571'}, // northeast: slash
+ {-1, 1, 0, '\u2571'}, // southwest: slash
+ {1, 1, 0, '\u2572'}, // southeast: backslash
{-1, -1, 0, '\u2572'}, // northwest: backslash
{0, 0, 1, '^'}, // up
{0, 0, -1, 'v'}, // down
diff --git a/modules/all-modules.go b/modules/all-modules.go
index bc8284556..c79eb6305 100644
--- a/modules/all-modules.go
+++ b/modules/all-modules.go
@@ -10,6 +10,7 @@ import (
_ "github.com/GoMudEngine/GoMud/modules/auctions"
_ "github.com/GoMudEngine/GoMud/modules/cleanup"
_ "github.com/GoMudEngine/GoMud/modules/clibridge"
+ _ "github.com/GoMudEngine/GoMud/modules/fasttravel"
_ "github.com/GoMudEngine/GoMud/modules/follow"
_ "github.com/GoMudEngine/GoMud/modules/gambling"
_ "github.com/GoMudEngine/GoMud/modules/gmcp"
diff --git a/modules/fasttravel/fasttravel.go b/modules/fasttravel/fasttravel.go
new file mode 100644
index 000000000..7b5582dc2
--- /dev/null
+++ b/modules/fasttravel/fasttravel.go
@@ -0,0 +1,290 @@
+package fasttravel
+
+import (
+ "embed"
+ "fmt"
+ "sort"
+ "strings"
+
+ "github.com/GoMudEngine/GoMud/internal/events"
+ "github.com/GoMudEngine/GoMud/internal/items"
+ "github.com/GoMudEngine/GoMud/internal/plugins"
+ "github.com/GoMudEngine/GoMud/internal/rooms"
+ "github.com/GoMudEngine/GoMud/internal/term"
+ "github.com/GoMudEngine/GoMud/internal/users"
+ "github.com/GoMudEngine/GoMud/internal/util"
+)
+
+var (
+ //go:embed files/*
+ files embed.FS
+)
+
+const (
+ fastTravelTag = "fast travel"
+ dataKeyFmt = "fasttravel-user-%d"
+)
+
+func init() {
+ m := &FastTravelModule{
+ plug: plugins.New(`fasttravel`, `1.0`),
+ players: make(map[int]FastTravelData),
+ }
+
+ if err := m.plug.AttachFileSystem(files); err != nil {
+ panic(err)
+ }
+
+ m.plug.AddUserCommand(`fasttravel`, m.fastTravelCommand, false, false)
+
+ m.plug.Web.AdminPage("Config", "fasttravel-config", "html/admin/fasttravel-config.html", true, "Modules", "Fast Travel", nil)
+
+ m.plug.ReserveTags(fastTravelTag)
+
+ m.plug.Callbacks.SetOnSave(m.onSave)
+
+ events.RegisterListener(events.PlayerSpawn{}, m.onPlayerSpawn)
+ events.RegisterListener(events.PlayerDespawn{}, m.onPlayerDespawn)
+
+ rooms.OnRoomLook.Register(m.onRoomLook)
+}
+
+// FastTravelData holds the unlocked fast travel room IDs for a single user.
+type FastTravelData struct {
+ UnlockedRoomIds []int `yaml:"unlockedroomids,omitempty"`
+}
+
+// FastTravelModule owns all fast travel state.
+type FastTravelModule struct {
+ plug *plugins.Plugin
+ players map[int]FastTravelData // keyed by userId; loaded on PlayerSpawn
+}
+
+func dataKey(userId int) string {
+ return fmt.Sprintf(dataKeyFmt, userId)
+}
+
+func (m *FastTravelModule) load(userId int) FastTravelData {
+ var data FastTravelData
+ m.plug.ReadIntoStruct(dataKey(userId), &data)
+ return data
+}
+
+func (m *FastTravelModule) save(userId int, data FastTravelData) {
+ m.plug.WriteStruct(dataKey(userId), data)
+}
+
+func (m *FastTravelModule) onSave() {
+ for userId, data := range m.players {
+ m.save(userId, data)
+ }
+}
+
+func (m *FastTravelModule) onPlayerSpawn(e events.Event) events.ListenerReturn {
+ evt, ok := e.(events.PlayerSpawn)
+ if !ok {
+ return events.Continue
+ }
+ m.players[evt.UserId] = m.load(evt.UserId)
+ return events.Continue
+}
+
+func (m *FastTravelModule) onPlayerDespawn(e events.Event) events.ListenerReturn {
+ evt, ok := e.(events.PlayerDespawn)
+ if !ok {
+ return events.Continue
+ }
+ if data, exists := m.players[evt.UserId]; exists {
+ m.save(evt.UserId, data)
+ delete(m.players, evt.UserId)
+ }
+ return events.Continue
+}
+
+// onRoomLook injects a fast travel alert when the room has the fast travel tag.
+func (m *FastTravelModule) onRoomLook(d rooms.RoomTemplateDetails) rooms.RoomTemplateDetails {
+ for _, t := range d.Tags {
+ if strings.EqualFold(t, fastTravelTag) {
+ d.RoomAlerts = append(d.RoomAlerts,
+ `
Configuration keys for the fasttravel module. Click any value to edit it inline. Changes are staged until you apply them.
+ + + +| Key | Value |
|---|---|
| Loading... | |