package game import ( "fmt" "sort" "strconv" "strings" "thehouseoficarus/internal/color" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" "thehouseoficarus/internal/world" ) func (g *Game) doLook(sess *net.Session) { p := sess.Player room, err := g.World.LoadRoom(p.RoomID) if err != nil { sess.WriteLine("You are in a void.") return } g.showRoomName(sess, p, room) wrapWidth := p.OptionInt("wrap_width") if wrapWidth < 80 { wrapWidth = 80 } var mapLines []string mapOn := p.OptionString("tiny_map") != "off" if mapOn { mapLines = buildTinyMap(g, sess, p.RoomID, mapGlyphsForPlayer(p.OptionBool("unicode"))) if len(mapLines) != 7 { mapLines = nil mapOn = false } } descWidth := wrapWidth if mapOn { descWidth = wrapWidth - 9 } var content []string content = append(content, g.showRoomDescription(sess, p, room, descWidth)...) content = append(content, g.showRoomObjects(sess, p, room)...) content = append(content, g.showRoomMobs(sess, p, room)...) content = append(content, g.showGroundItems(sess, p, room)...) if mapOn { g.writeLookSideBySide(sess, p, content, mapLines, descWidth) } else { for _, line := range content { sess.WriteLine(line) } } g.showRoomExits(sess, p, room) g.showRoomPlayers(sess, p, room) } func (g *Game) doExits(sess *net.Session) { p := sess.Player room, err := g.World.LoadRoom(p.RoomID) if err != nil || len(room.Exits) == 0 { sess.WriteLine("There are no exits here.") return } type exitLine struct { dir string name string } var lines []exitLine maxDirLen := 0 for _, dir := range world.ExitOrder { exitDef, ok := room.Exits[dir] if !ok { continue } coloredDir := g.colorize(sess, "exit_direction", string(dir)) targetRoom, err := g.World.LoadRoom(exitDef.Room) targetName := g.colorize(sess, "room_number", fmt.Sprintf("#%d", exitDef.Room)) if err == nil { targetName = g.colorize(sess, "exit_name", targetRoom.Name) } lines = append(lines, exitLine{coloredDir, targetName}) if visibleLen(coloredDir) > maxDirLen { maxDirLen = visibleLen(coloredDir) } } for _, l := range lines { pad := maxDirLen + (len(l.dir) - visibleLen(l.dir)) sess.WriteLine(fmt.Sprintf(" %-*s - %s", pad, l.dir, l.name)) } } func joinInts(nums []int) string { var parts []string for _, n := range nums { parts = append(parts, strconv.Itoa(n)) } return strings.Join(parts, ",") } func mobInstanceIdx(mob *world.MobInstance, roomMobs []*world.MobInstance) int { var same []*world.MobInstance for _, m := range roomMobs { if m.DefID == mob.DefID { same = append(same, m) } } if len(same) <= 1 { return 0 } sort.Slice(same, func(i, j int) bool { return same[i].InstanceID < same[j].InstanceID }) for i, m := range same { if m.InstanceID == mob.InstanceID { return i + 1 } } return 0 } func visibleLen(s string) int { return color.VisibleLen(s) } func wrapText(text string, width int) []string { if width <= 0 { return []string{text} } words := strings.Fields(text) if len(words) == 0 { return nil } var lines []string current := words[0] for _, word := range words[1:] { if visibleLen(current)+1+visibleLen(word) <= width { current += " " + word } else { lines = append(lines, current) current = word } } lines = append(lines, current) return lines } func (g *Game) writeLookSideBySide(sess *net.Session, p *player.Player, contentLines []string, mapLines []string, descWidth int) { total := len(contentLines) if len(mapLines) > total { total = len(mapLines) } leftMap := p.OptionString("tiny_map") == "left" mapWidth := descWidth if mapWidth <= 0 { mapWidth = 70 } for i := 0; i < total; i++ { content := "" if i < len(contentLines) { content = contentLines[i] } if i < len(mapLines) { mapLine := mapLines[i] if leftMap { sess.WriteLine(fmt.Sprintf("%s %s", mapLine, content)) } else { pad := mapWidth + (len(content) - visibleLen(content)) if pad < 0 { pad = 0 } sess.WriteLine(fmt.Sprintf("%-*s %s", pad, content, mapLine)) } } else { sess.WriteLine(content) } } } func (g *Game) executeLook(sess *net.Session, args []string, rawInput string) { if len(args) == 0 { g.doLook(sess) } else { g.doLookTarget(sess, strings.Join(args, " ")) } } func (g *Game) executeExits(sess *net.Session, args []string, rawInput string) { g.doExits(sess) }