aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cmd_walk.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-15 00:55:26 -0400
committerhistoria <[not public]>2026-06-15 00:56:27 -0400
commit445c4612cc5cf2c226f85894b6ad1e2419a374e2 (patch)
treedf3c6d4702cbeb7bd1618f88e93eb9cd08bcfcf4 /internal/game/cmd_walk.go
parentb6fdde0aa6fcefd735e7c550aaa7fca879023f1c (diff)
downloadthehouseoficarus-445c4612cc5cf2c226f85894b6ad1e2419a374e2.tar.gz
feat: walk command, improved tables and columns
Diffstat (limited to 'internal/game/cmd_walk.go')
-rw-r--r--internal/game/cmd_walk.go204
1 files changed, 204 insertions, 0 deletions
diff --git a/internal/game/cmd_walk.go b/internal/game/cmd_walk.go
new file mode 100644
index 0000000..31f9e18
--- /dev/null
+++ b/internal/game/cmd_walk.go
@@ -0,0 +1,204 @@
+package game
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+
+ "thirdcollapse/internal/net"
+ "thirdcollapse/internal/player"
+ "thirdcollapse/internal/world"
+)
+
+func (g *Game) doWalk(sess *net.Session, args []string) {
+ p := sess.Player.(*player.Player)
+ if len(args) == 0 {
+ sess.WriteLine("Walk where?")
+ return
+ }
+
+ input := strings.Join(args, "")
+ if roomID, err := strconv.Atoi(input); err == nil {
+ path := g.findPathToRoom(p.RoomID, roomID)
+ if path == nil {
+ sess.WriteLine(fmt.Sprintf("No path found to room #%d.", roomID))
+ return
+ }
+ g.startWalk(sess, p, path)
+ return
+ }
+
+ dirs, err := parseWalkDirections(input)
+ if err != nil {
+ sess.WriteLine(err.Error())
+ return
+ }
+ g.startWalk(sess, p, dirs)
+}
+
+func parseWalkDirections(input string) ([]string, error) {
+ var dirs []string
+ i := 0
+ for i < len(input) {
+ num := 0
+ for i < len(input) && input[i] >= '0' && input[i] <= '9' {
+ num = num*10 + int(input[i]-'0')
+ i++
+ }
+ if num == 0 {
+ num = 1
+ }
+ if i >= len(input) {
+ return nil, fmt.Errorf("Unexpected end of directions.")
+ }
+ ch := input[i]
+ i++
+ var dir string
+ switch ch {
+ case 'n':
+ dir = string(world.North)
+ case 's':
+ dir = string(world.South)
+ case 'e':
+ dir = string(world.East)
+ case 'w':
+ dir = string(world.West)
+ case 'u':
+ dir = string(world.Up)
+ case 'd':
+ dir = string(world.Down)
+ default:
+ return nil, fmt.Errorf("Unknown direction: %c", ch)
+ }
+ for j := 0; j < num; j++ {
+ dirs = append(dirs, dir)
+ }
+ }
+ return dirs, nil
+}
+
+func (g *Game) startWalk(sess *net.Session, p *player.Player, dirs []string) {
+ agilityLevel := p.Level(player.Agility)
+ if agilityLevel < 1 {
+ agilityLevel = 1
+ }
+ if len(dirs) > agilityLevel {
+ sess.WriteLine(fmt.Sprintf("You can only make %d moves at once with your current agility level!", agilityLevel))
+ return
+ }
+
+ g.CancelAction(p)
+ p.WalkSequence = dirs
+}
+
+func (g *Game) advanceWalk(sess *net.Session, p *player.Player) {
+ if len(p.WalkSequence) == 0 {
+ return
+ }
+
+ dir := p.WalkSequence[0]
+ oldRoom := p.RoomID
+ remaining := condensePath(p.WalkSequence[1:])
+ p.WalkSequence = p.WalkSequence[1:]
+
+ g.doMove(sess, dir)
+
+ if p.RoomID == oldRoom {
+ p.WalkSequence = nil
+ p.ActionState = nil
+ return
+ }
+
+ if len(p.WalkSequence) > 0 && remaining != "" {
+ p.ActionState = &ActionState{Type: ActionWalking}
+ sess.WriteLine(fmt.Sprintf("You're headed: %s.", remaining))
+ }
+}
+
+func condensePath(dirs []string) string {
+ if len(dirs) == 0 {
+ return ""
+ }
+ var result strings.Builder
+ i := 0
+ for i < len(dirs) {
+ count := 1
+ for i+count < len(dirs) && dirs[i+count] == dirs[i] {
+ count++
+ }
+ if count > 1 {
+ result.WriteString(fmt.Sprintf("%d", count))
+ }
+ result.WriteString(directionToChar(dirs[i]))
+ i += count
+ }
+ return result.String()
+}
+
+func directionToChar(dir string) string {
+ switch world.ExitDir(dir) {
+ case world.North:
+ return "n"
+ case world.South:
+ return "s"
+ case world.East:
+ return "e"
+ case world.West:
+ return "w"
+ case world.Up:
+ return "u"
+ case world.Down:
+ return "d"
+ }
+ return "?"
+}
+
+var bfsSearchDirs = []world.ExitDir{world.North, world.South, world.East, world.West, world.Up, world.Down}
+
+func (g *Game) findPathToRoom(fromRoom, toRoom int) []string {
+ if fromRoom == toRoom {
+ return nil
+ }
+
+ type bfsNode struct {
+ roomID int
+ path []string
+ }
+
+ visited := make(map[int]bool)
+ queue := []bfsNode{{fromRoom, nil}}
+ visited[fromRoom] = true
+
+ for len(queue) > 0 {
+ cur := queue[0]
+ queue = queue[1:]
+
+ room, ok := loadRoom(g, cur.roomID)
+ if !ok {
+ continue
+ }
+
+ for _, dir := range bfsSearchDirs {
+ exitDef, exists := room.Exits[dir]
+ if !exists {
+ continue
+ }
+ if visited[exitDef.Room] {
+ continue
+ }
+
+ newPath := make([]string, len(cur.path)+1)
+ copy(newPath, cur.path)
+ newPath[len(cur.path)] = string(dir)
+
+ if exitDef.Room == toRoom {
+ return newPath
+ }
+
+ visited[exitDef.Room] = true
+ queue = append(queue, bfsNode{exitDef.Room, newPath})
+ }
+ }
+
+ return nil
+}