aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cmd_look.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-16 01:59:27 -0400
committerhistoria <[not public]>2026-06-16 01:59:27 -0400
commit43f21aabae8924f35c12c8485e690aeefe0089fb (patch)
treeb5fbadb89df3cf4ffec86503bb8e2d7e52b27454 /internal/game/cmd_look.go
parent4cc1ddcd185509080b4b3e15d1646567edd51c9d (diff)
downloadthehouseoficarus-43f21aabae8924f35c12c8485e690aeefe0089fb.tar.gz
feat: overhauled ansi color, added prompt options
Diffstat (limited to 'internal/game/cmd_look.go')
-rw-r--r--internal/game/cmd_look.go124
1 files changed, 94 insertions, 30 deletions
diff --git a/internal/game/cmd_look.go b/internal/game/cmd_look.go
index 992f3d0..bd738be 100644
--- a/internal/game/cmd_look.go
+++ b/internal/game/cmd_look.go
@@ -2,13 +2,14 @@ package game
import (
"fmt"
+ "regexp"
"sort"
"strconv"
"strings"
- "thirdcollapse/internal/color"
"thirdcollapse/internal/combat"
"thirdcollapse/internal/net"
+ "thirdcollapse/internal/object"
"thirdcollapse/internal/player"
"thirdcollapse/internal/world"
)
@@ -21,17 +22,20 @@ func (g *Game) doLook(sess *net.Session) {
return
}
- mode := g.colorMode(sess)
sess.WriteLines(
"",
- fmt.Sprintf("%s (#%d)", color.Wrap(mode, "cyan", room.Name), room.ID),
+ fmt.Sprintf("%s (%s)", g.colorize(sess, "room_name", room.Name), g.colorize(sess, "room_number", fmt.Sprintf("#%d", room.ID))),
)
descWidth := p.OptionInt("room_desc_width")
if descWidth <= 0 {
descWidth = 70
}
- descLines := wrapText(g.colorTag(sess, room.Description), descWidth)
+ rawLines := wrapText(room.Description, descWidth)
+ descLines := make([]string, len(rawLines))
+ for i, l := range rawLines {
+ descLines[i] = g.colorize(sess, "room_desc", l)
+ }
wroteDesc := false
if p.OptionString("tiny_map") != "off" {
mapLines := buildTinyMap(g, p.RoomID, mapGlyphsForPlayer(p.OptionBool("unicode")))
@@ -46,7 +50,7 @@ func (g *Game) doLook(sess *net.Session) {
}
}
- mobs := g.MobStore.MobsInRoom(p.RoomID)
+ mobs := g.MobStore.MobsInRoom(p.RoomID)
if len(mobs) > 0 {
sort.Slice(mobs, func(i, j int) bool {
iDamaged := mobs[i].HP < mobs[i].MaxHP
@@ -57,10 +61,11 @@ func (g *Game) doLook(sess *net.Session) {
return mobs[i].InstanceID < mobs[j].InstanceID
})
sess.WriteLine("")
+ playerLevel := p.CombatLevel()
for _, m := range mobs {
hp := ""
if m.HP < m.MaxHP {
- hp = fmt.Sprintf(" [%d/%dhp]", m.HP, m.MaxHP)
+ hp = fmt.Sprintf(" [%s/%dhp]", g.colorize(sess, "enemy_hp", fmt.Sprint(m.HP)), m.MaxHP)
}
var desc string
if combat.IsMobInCombat(m.InstanceID) {
@@ -77,7 +82,13 @@ func (g *Game) doLook(sess *net.Session) {
if !m.Unique {
displayName = "A " + m.Name
}
- sess.WriteLine(fmt.Sprintf(" %s (level %d)%s%s", displayName, mobCombatLevel(m), hp, desc))
+ npcColor := "hostile_npc"
+ if m.Protected {
+ npcColor = "friendly_npc"
+ }
+ mobLevel := mobCombatLevel(m)
+ levelStr := g.levelColorize(sess, playerLevel, mobLevel, fmt.Sprintf("(level %d)", mobLevel))
+ sess.WriteLine(fmt.Sprintf(" %s %s%s%s", g.colorize(sess, npcColor, displayName), levelStr, hp, desc))
}
}
@@ -138,15 +149,17 @@ func (g *Game) doLook(sess *net.Session) {
}
roomDesc := def.InRoomDescription
+ coloredName := g.objColorize(sess, def, def.Name)
+ coloredPlural := g.objColorize(sess, def, def.Name+"s")
if len(freshIdxs) > 0 {
var line string
if roomDesc != "" {
line = roomDesc
} else if len(freshIdxs) == 1 {
- line = "A " + def.Name + " is here."
+ line = fmt.Sprintf("A %s is here.", coloredName)
} else {
- line = fmt.Sprintf("%d %ss are here.", len(freshIdxs), def.Name)
+ line = fmt.Sprintf("%d %s are here.", len(freshIdxs), coloredPlural)
}
var suffix string
if multi && (len(timed) > 0 || len(depleted) > 0) {
@@ -164,7 +177,7 @@ func (g *Game) doLook(sess *net.Session) {
if roomDesc != "" {
line = roomDesc
} else {
- line = "A " + def.Name + " is here."
+ line = fmt.Sprintf("A %s is here.", coloredName)
}
tag := ""
if multi {
@@ -182,7 +195,7 @@ func (g *Game) doLook(sess *net.Session) {
if roomDesc != "" {
line = roomDesc
} else {
- line = "A " + def.Name + " is here."
+ line = fmt.Sprintf("A %s is here.", coloredName)
}
tag := ""
if multi {
@@ -227,14 +240,15 @@ func (g *Game) doLook(sess *net.Session) {
if err == nil {
name = def.Name
}
+ coloredName := g.itemColorize(sess, def, name)
prefix := ""
if info.Quantity > 1 {
- prefix = fmt.Sprintf(" %d x %s", info.Quantity, name)
+ prefix = fmt.Sprintf(" %d x %s", info.Quantity, coloredName)
} else {
- prefix = fmt.Sprintf(" %s", name)
+ prefix = fmt.Sprintf(" %s", coloredName)
}
- if len(prefix) > maxPrefix {
- maxPrefix = len(prefix)
+ if visibleLen(prefix) > maxPrefix {
+ maxPrefix = visibleLen(prefix)
}
var parts []string
if info.ReservedFor != "" {
@@ -256,7 +270,8 @@ func (g *Game) doLook(sess *net.Session) {
for _, l := range lines {
if l.reserved != "" {
- sess.WriteLine(fmt.Sprintf("%-*s%s", maxPrefix+1, l.prefix, l.reserved))
+ pad := maxPrefix + 1 + (len(l.prefix) - visibleLen(l.prefix))
+ sess.WriteLine(fmt.Sprintf("%-*s%s", pad, l.prefix, l.reserved))
} else {
sess.WriteLine(l.prefix)
}
@@ -265,22 +280,36 @@ func (g *Game) doLook(sess *net.Session) {
if len(room.Exits) > 0 {
sess.WriteLine("")
- if p.OptionBool("exits") {
+ if p.OptionBool("exits") {
sess.WriteLine("Exits:")
+ type exitLine struct {
+ dir string
+ targetName 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 := fmt.Sprintf("#%d", exitDef.Room)
+ targetName := g.colorize(sess, "room_number", fmt.Sprintf("#%d", exitDef.Room))
if err == nil {
- targetName = targetRoom.Name
+ targetName = g.colorize(sess, "exit_name", targetRoom.Name)
}
if exitDef.Condition != nil && !g.checkCondition(sess, exitDef.Condition) {
targetName += " (blocked)"
}
- sess.WriteLine(fmt.Sprintf(" %-6s - %s", dir, targetName))
+ 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.targetName))
}
} else {
sess.Write("Exits: ")
@@ -290,7 +319,7 @@ func (g *Game) doLook(sess *net.Session) {
if !first {
sess.Write(", ")
}
- sess.Write(string(dir))
+ sess.Write(g.colorize(sess, "exit_direction", string(dir)))
first = false
}
}
@@ -355,9 +384,15 @@ func (g *Game) doLookTarget(sess *net.Session, input string) {
}
}
if best != nil {
+ npcColor := "hostile_npc"
+ if best.Protected {
+ npcColor = "friendly_npc"
+ }
+ mobLevel := mobCombatLevel(best)
+ levelStr := g.levelColorize(sess, p.CombatLevel(), mobLevel, fmt.Sprintf("(level %d)", mobLevel))
sess.WriteLines(
"",
- fmt.Sprintf("%s (level %d)", best.Name, mobCombatLevel(best)),
+ fmt.Sprintf("%s %s", g.colorize(sess, npcColor, best.Name), levelStr),
)
if best.IdleDescription != "" {
sess.WriteLine(fmt.Sprintf(" %s", best.IdleDescription))
@@ -423,7 +458,7 @@ func (g *Game) doLookTarget(sess *net.Session, input string) {
}
sess.WriteLines(
"",
- def.Name,
+ g.itemColorize(sess, def, def.Name),
fmt.Sprintf(" %s", def.Description),
fmt.Sprintf(" Value: %d credits", def.Value),
)
@@ -469,10 +504,13 @@ func (g *Game) doLookTarget(sess *net.Session, input string) {
}
func showPlayerInfo(g *Game, sess *net.Session, p *player.Player) {
+ myP := sess.Player.(*player.Player)
+ theirLevel := p.CombatLevel()
+ levelStr := g.levelColorize(sess, myP.CombatLevel(), theirLevel, fmt.Sprint(theirLevel))
sess.WriteLines(
"",
p.Name,
- fmt.Sprintf(" Combat Level: %d", p.CombatLevel()),
+ fmt.Sprintf(" Combat Level: %s", levelStr),
fmt.Sprintf(" HP: %d/%d", p.HP, p.MaxHP()),
"",
)
@@ -490,10 +528,12 @@ func showPlayerInfo(g *Game, sess *net.Session, p *player.Player) {
continue
}
name := itemID
+ var itemDef *object.ItemDef
if def, err := g.ItemStore.Load(itemID); err == nil {
name = def.Name
+ itemDef = def
}
- sess.WriteLine(fmt.Sprintf(" %-12s %s", slot, name))
+ sess.WriteLine(fmt.Sprintf(" %-12s %s", slot, g.itemColorize(sess, itemDef, name)))
}
if p.Description != "" {
@@ -509,17 +549,31 @@ func (g *Game) doExits(sess *net.Session) {
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 := fmt.Sprintf("#%d", exitDef.Room)
+ targetName := g.colorize(sess, "room_number", fmt.Sprintf("#%d", exitDef.Room))
if err == nil {
- targetName = targetRoom.Name
+ targetName = g.colorize(sess, "exit_name", targetRoom.Name)
+ }
+ lines = append(lines, exitLine{coloredDir, targetName})
+ if visibleLen(coloredDir) > maxDirLen {
+ maxDirLen = visibleLen(coloredDir)
}
- sess.WriteLine(fmt.Sprintf(" %-6s - %s", dir, targetName))
+ }
+ for _, l := range lines {
+ pad := maxDirLen + (len(l.dir) - visibleLen(l.dir))
+ sess.WriteLine(fmt.Sprintf(" %-*s - %s", pad, l.dir, l.name))
}
}
@@ -552,6 +606,12 @@ func mobInstanceIdx(mob *world.MobInstance, roomMobs []*world.MobInstance) int {
return 0
}
+var ansiRe = regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
+
+func visibleLen(s string) int {
+ return len(ansiRe.ReplaceAllString(s, ""))
+}
+
func wrapText(text string, width int) []string {
if width <= 0 {
return []string{text}
@@ -563,7 +623,7 @@ func wrapText(text string, width int) []string {
var lines []string
current := words[0]
for _, word := range words[1:] {
- if len(current)+1+len(word) <= width {
+ if visibleLen(current)+1+visibleLen(word) <= width {
current += " " + word
} else {
lines = append(lines, current)
@@ -596,7 +656,11 @@ func (g *Game) writeLookSideBySide(sess *net.Session, p *player.Player, descLine
if leftMap {
sess.WriteLine(fmt.Sprintf("%s %s", mapLine, desc))
} else {
- sess.WriteLine(fmt.Sprintf("%-*s %s", mapWidth, desc, mapLine))
+ pad := mapWidth + (len(desc) - visibleLen(desc))
+ if pad < 0 {
+ pad = 0
+ }
+ sess.WriteLine(fmt.Sprintf("%-*s %s", pad, desc, mapLine))
}
}
}