package game import ( "fmt" "strconv" "strings" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" "thehouseoficarus/internal/world" ) func (g *Game) executeWalk(sess *net.Session, args []string, rawInput string) { g.doWalk(sess, args) } func (g *Game) doWalk(sess *net.Session, args []string) { p := sess.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 p.MoveTicks > 0 { return } if len(p.WalkSequence) == 0 { return } dir := p.WalkSequence[0] remaining := condensePath(p.WalkSequence[1:]) p.WalkSequence = p.WalkSequence[1:] oldRoom := p.RoomID g.doMove(sess, dir, 2.0) if p.MoveTicks == 0 && p.RoomID == oldRoom { p.WalkSequence = nil return } if len(p.WalkSequence) > 0 && remaining != "" { 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 }