diff options
| author | historia <[not public]> | 2026-06-15 00:55:26 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-15 00:56:27 -0400 |
| commit | 445c4612cc5cf2c226f85894b6ad1e2419a374e2 (patch) | |
| tree | df3c6d4702cbeb7bd1618f88e93eb9cd08bcfcf4 /internal/game/cmd_walk.go | |
| parent | b6fdde0aa6fcefd735e7c550aaa7fca879023f1c (diff) | |
| download | thehouseoficarus-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.go | 204 |
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 +} |
