aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/help/room_desc_width.yaml14
-rw-r--r--internal/color/color.go31
-rw-r--r--internal/game/cmd_aps.go2
-rw-r--r--internal/game/cmd_bank.go2
-rw-r--r--internal/game/cmd_color.go2
-rw-r--r--internal/game/cmd_look.go24
-rw-r--r--internal/game/cmd_mods.go2
-rw-r--r--internal/game/cmd_option.go2
-rw-r--r--internal/game/cmd_shop.go4
-rw-r--r--internal/game/cmd_skills.go2
-rw-r--r--internal/game/cmd_tech.go2
-rw-r--r--internal/game/look_room.go3
-rw-r--r--internal/game/production_menu.go4
-rw-r--r--internal/game/render_help.go4
-rw-r--r--internal/player/player.go3
-rw-r--r--internal/ui/table.go30
16 files changed, 95 insertions, 36 deletions
diff --git a/data/help/room_desc_width.yaml b/data/help/room_desc_width.yaml
deleted file mode 100644
index 7c67a38..0000000
--- a/data/help/room_desc_width.yaml
+++ /dev/null
@@ -1,14 +0,0 @@
-name: "room_desc_width"
-category: "Options"
-description: |
- Maximum width for room description text when displayed alongside
- the mini-map.
-
- Type: integer
- Default: 70
-
- When the tinymap option is enabled, room descriptions are wrapped
- to this width and displayed side-by-side with the mini-map.
-
- Usage: option room_desc_width 80
- option room_desc_width 50
diff --git a/internal/color/color.go b/internal/color/color.go
index e971382..5a1aa54 100644
--- a/internal/color/color.go
+++ b/internal/color/color.go
@@ -180,6 +180,37 @@ func VisibleLen(s string) int {
return utf8.RuneCountInString(ansiRe.ReplaceAllString(s, ""))
}
+// TruncateVisible truncates s to at most maxVisible visible characters,
+// preserving ANSI SGR codes. Assumes cells follow the common pattern
+// "\x1b[...mTEXT\x1b[0m" or are plain text. Appends "..." when truncated.
+func TruncateVisible(s string, maxVisible int) string {
+ if maxVisible < 3 {
+ maxVisible = 3
+ }
+ if VisibleLen(s) <= maxVisible {
+ return s
+ }
+ var pre, suf string
+ rest := s
+ if idx := strings.IndexByte(rest, 'm'); idx >= 0 && strings.HasPrefix(rest, "\x1b[") {
+ pre = rest[:idx+1]
+ rest = rest[idx+1:]
+ }
+ if strings.HasSuffix(rest, Reset) {
+ suf = Reset
+ rest = rest[:len(rest)-len(Reset)]
+ }
+ runes := []rune(rest)
+ budget := maxVisible - 3
+ if budget < 0 {
+ budget = 0
+ }
+ if len(runes) > budget {
+ rest = string(runes[:budget]) + "..."
+ }
+ return pre + rest + suf
+}
+
// WrapANSI wraps text to the given visible width, ignoring ANSI color codes
// when measuring. Each input line (split on "\n") is handled on its own: a line
// that already fits is returned untouched, so indentation and aligned UI are
diff --git a/internal/game/cmd_aps.go b/internal/game/cmd_aps.go
index 5e209a6..a5cff78 100644
--- a/internal/game/cmd_aps.go
+++ b/internal/game/cmd_aps.go
@@ -148,7 +148,7 @@ func displayApsNodes(g *Game, sess *net.Session, p *player.Player, known []int)
Rows: rows,
}
- for _, line := range t.Render(p.OptionBool("unicode")) {
+ for _, line := range t.Render(p.OptionBool("unicode"), p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
}
diff --git a/internal/game/cmd_bank.go b/internal/game/cmd_bank.go
index c489ad8..bbf600f 100644
--- a/internal/game/cmd_bank.go
+++ b/internal/game/cmd_bank.go
@@ -110,7 +110,7 @@ func (g *Game) showBankBrowse(sess *net.Session) {
})
}
- for _, line := range t.Render(unicode) {
+ for _, line := range t.Render(unicode, p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
}
diff --git a/internal/game/cmd_color.go b/internal/game/cmd_color.go
index 8e90f02..00b4ce8 100644
--- a/internal/game/cmd_color.go
+++ b/internal/game/cmd_color.go
@@ -193,7 +193,7 @@ func showColorTable(g *Game, sess *net.Session) {
color.Render(mode, color.Parse("F3"), source),
})
}
- for _, line := range table.Render(p.OptionBool("unicode")) {
+ for _, line := range table.Render(p.OptionBool("unicode"), p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
}
diff --git a/internal/game/cmd_look.go b/internal/game/cmd_look.go
index a1ad708..c34052a 100644
--- a/internal/game/cmd_look.go
+++ b/internal/game/cmd_look.go
@@ -22,22 +22,34 @@ func (g *Game) doLook(sess *net.Session) {
g.showRoomName(sess, p, room)
+ wrapWidth := p.OptionInt("wrap_width")
+ if wrapWidth < 80 {
+ wrapWidth = 80
+ }
+
var mapLines []string
- if p.OptionString("tiny_map") != "off" {
+ 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)...)
+ 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 len(mapLines) == 7 {
- g.writeLookSideBySide(sess, p, content, mapLines)
+ if mapOn {
+ g.writeLookSideBySide(sess, p, content, mapLines, descWidth)
} else {
for _, line := range content {
sess.WriteLine(line)
@@ -138,13 +150,13 @@ func wrapText(text string, width int) []string {
return lines
}
-func (g *Game) writeLookSideBySide(sess *net.Session, p *player.Player, contentLines []string, mapLines []string) {
+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 := p.OptionInt("room_desc_width")
+ mapWidth := descWidth
if mapWidth <= 0 {
mapWidth = 70
}
diff --git a/internal/game/cmd_mods.go b/internal/game/cmd_mods.go
index 4f3f9ff..9468e89 100644
--- a/internal/game/cmd_mods.go
+++ b/internal/game/cmd_mods.go
@@ -92,7 +92,7 @@ func (g *Game) doMods(sess *net.Session, showAll bool) {
}
}
- for _, line := range t.Render(p.OptionBool("unicode")) {
+ for _, line := range t.Render(p.OptionBool("unicode"), p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
sess.WriteLine("")
diff --git a/internal/game/cmd_option.go b/internal/game/cmd_option.go
index a2e54c2..958fc0b 100644
--- a/internal/game/cmd_option.go
+++ b/internal/game/cmd_option.go
@@ -35,7 +35,7 @@ func (g *Game) doOption(sess *net.Session, input string) {
color.Render(mode, color.Parse("FC"), def.Description),
})
}
- for _, line := range table.Render(p.OptionBool("unicode")) {
+ for _, line := range table.Render(p.OptionBool("unicode"), p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
return
diff --git a/internal/game/cmd_shop.go b/internal/game/cmd_shop.go
index 91a11c6..69de750 100644
--- a/internal/game/cmd_shop.go
+++ b/internal/game/cmd_shop.go
@@ -118,8 +118,10 @@ func (g *Game) showShopBrowse(sess *net.Session) {
}
unicode := true
+ wrapWidth := 0
if p := sess.Player; p != nil {
unicode = p.OptionBool("unicode")
+ wrapWidth = p.OptionInt("wrap_width")
}
t := ui.Table{
@@ -145,7 +147,7 @@ func (g *Game) showShopBrowse(sess *net.Session) {
})
}
- for _, line := range t.Render(unicode) {
+ for _, line := range t.Render(unicode, wrapWidth) {
sess.WriteLine(line)
}
}
diff --git a/internal/game/cmd_skills.go b/internal/game/cmd_skills.go
index 9321b96..4aacd31 100644
--- a/internal/game/cmd_skills.go
+++ b/internal/game/cmd_skills.go
@@ -35,7 +35,7 @@ func (g *Game) doSkills(sess *net.Session) {
color.Render(mode, color.Parse("B3"), strconv.Itoa(next)),
})
}
- for _, line := range t.Render(p.OptionBool("unicode")) {
+ for _, line := range t.Render(p.OptionBool("unicode"), p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
}
diff --git a/internal/game/cmd_tech.go b/internal/game/cmd_tech.go
index 8c6f14f..cb0465f 100644
--- a/internal/game/cmd_tech.go
+++ b/internal/game/cmd_tech.go
@@ -150,7 +150,7 @@ func (g *Game) doTechList(sess *net.Session) {
})
}
- for _, line := range t.Render(p.OptionBool("unicode")) {
+ for _, line := range t.Render(p.OptionBool("unicode"), p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
diff --git a/internal/game/look_room.go b/internal/game/look_room.go
index 585efb0..f35d564 100644
--- a/internal/game/look_room.go
+++ b/internal/game/look_room.go
@@ -35,8 +35,7 @@ func (g *Game) roomSymbolBracket(sess *net.Session, p *player.Player, room *worl
color.Render(mode, dimSpec, "]")
}
-func (g *Game) showRoomDescription(sess *net.Session, p *player.Player, room *world.Room) []string {
- descWidth := p.OptionInt("room_desc_width")
+func (g *Game) showRoomDescription(sess *net.Session, p *player.Player, room *world.Room, descWidth int) []string {
if descWidth <= 0 {
descWidth = 70
}
diff --git a/internal/game/production_menu.go b/internal/game/production_menu.go
index f625d54..bc2fccc 100644
--- a/internal/game/production_menu.go
+++ b/internal/game/production_menu.go
@@ -169,7 +169,7 @@ func (g *Game) showProductionTable(sess *net.Session, p *player.Player, items []
unicode := p.OptionBool("unicode")
sess.WriteLine("")
- for _, line := range tbl.Render(unicode) {
+ for _, line := range tbl.Render(unicode, p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
@@ -350,7 +350,7 @@ func (g *Game) showMenuTable(sess *net.Session, title string, names []string) {
}
unicode := p.OptionBool("unicode")
sess.WriteLine("")
- for _, line := range tbl.Render(unicode) {
+ for _, line := range tbl.Render(unicode, p.OptionInt("wrap_width")) {
sess.WriteLine(line)
}
}
diff --git a/internal/game/render_help.go b/internal/game/render_help.go
index 869d306..798eadf 100644
--- a/internal/game/render_help.go
+++ b/internal/game/render_help.go
@@ -100,8 +100,10 @@ func LoadHelp(dataDir string) ([]HelpDef, error) {
func (g *Game) doHelp(sess *net.Session, topic string) {
if topic == "" {
unicode := true
+ wrapWidth := 0
if p := sess.Player; p != nil {
unicode = p.OptionBool("unicode")
+ wrapWidth = p.OptionInt("wrap_width")
}
sess.WriteLine("")
@@ -112,7 +114,7 @@ func (g *Game) doHelp(sess *net.Session, topic string) {
for _, c := range commandList {
t.Rows = append(t.Rows, []string{c.Name, c.Type, c.Desc})
}
- for _, line := range t.Render(unicode) {
+ for _, line := range t.Render(unicode, wrapWidth) {
sess.WriteLine(line)
}
sess.WriteLine("")
diff --git a/internal/player/player.go b/internal/player/player.go
index 845dbb0..efe13e2 100644
--- a/internal/player/player.go
+++ b/internal/player/player.go
@@ -125,8 +125,7 @@ var OptionDefs = []OptionDef{
{"map_padding", OptString, "none", []string{"none", "x", "y", "xy"}, "Map output padding mode"},
{"automap", OptBool, false, nil, "Show map automatically after moving"},
{"show_queued_cmds", OptBool, false, nil, "Show confirmation messages for queued tick actions"},
- {"room_desc_width", OptInt, 70, nil, "Maximum width for room descriptions"},
- {"wrap_width", OptInt, 120, nil, "Wrap all output to this many columns (minimum 80)"},
+ {"wrap_width", OptInt, 80, nil, "Wrap all output to this many columns (minimum 80)"},
{"unicode", OptBool, true, nil, "Unicode box-drawing characters"},
{"run_countdown", OptBool, false, nil, "Show countdown messages when fleeing combat"},
{"visual_ticks", OptBool, false, nil, "Display a tick marker every game tick"},
diff --git a/internal/ui/table.go b/internal/ui/table.go
index 0ac41ad..5061417 100644
--- a/internal/ui/table.go
+++ b/internal/ui/table.go
@@ -38,7 +38,7 @@ type Table struct {
Rows [][]string
}
-func (t *Table) Render(unicode bool) []string {
+func (t *Table) Render(unicode bool, maxWidth int) []string {
g := tableGlyphSet(unicode)
nCols := len(t.Columns)
@@ -65,6 +65,34 @@ func (t *Table) Render(unicode bool) []string {
}
}
+ if maxWidth > 0 {
+ overhead := 3*nCols + 1
+ total := overhead
+ for _, w := range colWidths {
+ total += w
+ }
+ if total > maxWidth {
+ last := nCols - 1
+ budget := maxWidth - overhead
+ for i := 0; i < last; i++ {
+ budget -= colWidths[i]
+ }
+ if budget < 5 {
+ budget = 5
+ }
+ colWidths[last] = budget
+
+ if len(t.Columns) > last {
+ t.Columns[last] = color.TruncateVisible(t.Columns[last], budget)
+ }
+ for i := range t.Rows {
+ if len(t.Rows[i]) > last {
+ t.Rows[i][last] = color.TruncateVisible(t.Rows[i][last], budget)
+ }
+ }
+ }
+ }
+
makeSep := func(left, cross, right rune, fill rune) string {
var b strings.Builder
b.WriteRune(left)