diff options
| author | historia <[not public]> | 2026-06-16 23:37:50 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-16 23:37:50 -0400 |
| commit | 389e307f205f9b7edf604fb9f86e1038ead736ef (patch) | |
| tree | 595095b5877cc2b0513d31fb126303d8796cc311 /internal/game | |
| parent | 085e728d22a3369bd110b77409914cfbe9ebe611 (diff) | |
| download | thehouseoficarus-389e307f205f9b7edf604fb9f86e1038ead736ef.tar.gz | |
feat: major xterm256 color overhaul, see worldbuilding docs.
Diffstat (limited to 'internal/game')
47 files changed, 555 insertions, 303 deletions
diff --git a/internal/game/action.go b/internal/game/action.go index 3857030..04e2b0a 100644 --- a/internal/game/action.go +++ b/internal/game/action.go @@ -6,12 +6,12 @@ import ( "strconv" "strings" - "thirdcollapse/internal/action" - "thirdcollapse/internal/combat" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) var verbAliases = map[string]string{ diff --git a/internal/game/action_burn.go b/internal/game/action_burn.go index 5c19510..01bf385 100644 --- a/internal/game/action_burn.go +++ b/internal/game/action_burn.go @@ -4,11 +4,11 @@ import ( "fmt" "math/rand" - "thirdcollapse/internal/action" - "thirdcollapse/internal/engine" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" ) func (g *Game) doBurn(sess *net.Session, p *player.Player, input string) { @@ -70,13 +70,13 @@ func (g *Game) startBurn(sess *net.Session, p *player.Player, itemID string, fro } if logDef.BurnTicks <= 0 { - sess.WriteLine(fmt.Sprintf("You can't burn the %s.", logDef.Name)) + sess.WriteLine(fmt.Sprintf("You can't burn the %s.", g.itemColorize(sess, logDef, logDef.Name))) return } skillLevel := p.Level(player.Firemaking) if logDef.FireLevel > 0 && skillLevel < logDef.FireLevel { - sess.WriteLine(fmt.Sprintf("You need level %d firemaking to burn %s.", logDef.FireLevel, logDef.Name)) + sess.WriteLine(fmt.Sprintf("You need level %d firemaking to burn %s.", logDef.FireLevel, g.itemColorize(sess, logDef, logDef.Name))) return } @@ -87,13 +87,13 @@ func (g *Game) startBurn(sess *net.Session, p *player.Player, itemID string, fro if !fromGround { if !p.HasItem(itemID) { - sess.WriteLine(fmt.Sprintf("You don't have any %s.", logDef.Name)) + sess.WriteLine(fmt.Sprintf("You don't have any %s.", g.itemColorize(sess, logDef, logDef.Name))) return } } else { ground := g.World.GroundItems(p.RoomID) if ground[itemID] <= 0 { - sess.WriteLine(fmt.Sprintf("There are no %s on the ground here.", logDef.Name)) + sess.WriteLine(fmt.Sprintf("There are no %s on the ground here.", g.itemColorize(sess, logDef, logDef.Name))) return } } @@ -135,7 +135,7 @@ func (g *Game) startStoke(sess *net.Session, p *player.Player, itemID string) { } if !p.HasItem(itemID) { - sess.WriteLine(fmt.Sprintf("You don't have any %s.", logDef.Name)) + sess.WriteLine(fmt.Sprintf("You don't have any %s.", g.itemColorize(sess, logDef, logDef.Name))) return } @@ -171,7 +171,7 @@ func (g *Game) advanceBurn(sess *net.Session, p *player.Player) { if phase == 0 { if !p.HasItem(itemID) { - sess.WriteLine(fmt.Sprintf("You're out of %s.", logDef.Name)) + sess.WriteLine(fmt.Sprintf("You're out of %s.", g.itemColorize(sess, logDef, logDef.Name))) g.CancelAction(p) return } @@ -179,7 +179,7 @@ func (g *Game) advanceBurn(sess *net.Session, p *player.Player) { g.World.AddGroundItem(p.RoomID, itemID, 1) g.AccountStore.SaveCharacter(p) - sess.WriteLine(fmt.Sprintf("You drop the %s and begin to make a fire...", logDef.Name)) + sess.WriteLine(g.colorize(sess, "fire", fmt.Sprintf("You drop the %s and begin to make a fire...", g.itemColorize(sess, logDef, logDef.Name)))) g.broadcastDrop(p, logDef.Name) data["phase"] = 1 @@ -195,7 +195,7 @@ func (g *Game) advanceBurn(sess *net.Session, p *player.Player) { } if phase == 1 { - sess.WriteLine(fmt.Sprintf("You try burning the %s on the ground...", logDef.Name)) + sess.WriteLine(g.colorize(sess, "fire", fmt.Sprintf("You try burning the %s on the ground...", g.itemColorize(sess, logDef, logDef.Name)))) data["phase"] = 2 p.Action.WaitLeft = engine.ToTicks(toolSpeed) return @@ -216,26 +216,26 @@ func (g *Game) advanceBurn(sess *net.Session, p *player.Player) { g.World.AddObjInstance(p.RoomID, "fire", logDef.BurnTicks) xp := logDef.FireXP - if xp > 0 { - if newLevel := p.AddSkillXP(player.Firemaking, xp); newLevel > 0 { - sess.WriteLine(fmt.Sprintf("*** You are now level %d firemaking! ***", newLevel)) - } + if xp > 0 { + if newLevel := p.AddSkillXP(player.Firemaking, xp); newLevel > 0 { + sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d firemaking! ***", newLevel))) } + } - g.AccountStore.SaveCharacter(p) - g.CancelAction(p) + g.AccountStore.SaveCharacter(p) + g.CancelAction(p) - msg := fmt.Sprintf("You manage to get a fire going!") - if xp > 0 && p.OptionBool("xp_drops") { - msg += fmt.Sprintf(" (+%dxp %s)", xp, player.SkillAbbr[player.Firemaking]) - } - sess.WriteLine(msg) + msg := g.colorize(sess, "fire", "You manage to get a fire going!") + if xp > 0 && p.OptionBool("xp_drops") { + msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", xp, player.SkillAbbr[player.Firemaking])) + } + sess.WriteLine(msg) - for _, other := range g.Hub.PlayersInRoom(p.RoomID) { - if other != sess { - other.WriteLine(fmt.Sprintf("\n%s started a fire!", p.Name)) - } + for _, other := range g.Hub.PlayersInRoom(p.RoomID) { + if other != sess { + other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s started a fire!", p.Name))) } + } return } @@ -266,7 +266,7 @@ func (g *Game) advanceStoke(sess *net.Session, p *player.Player) { } if !p.HasItem(itemID) { - sess.WriteLine(fmt.Sprintf("You're out of %s and the fire is roaring.", name)) + sess.WriteLine(g.colorize(sess, "fire", fmt.Sprintf("You're out of %s and the fire is roaring.", g.itemColorize(sess, logDef, name)))) g.CancelAction(p) return } @@ -276,20 +276,20 @@ func (g *Game) advanceStoke(sess *net.Session, p *player.Player) { if xp > 0 { if newLevel := p.AddSkillXP(player.Firemaking, xp); newLevel > 0 { - sess.WriteLine(fmt.Sprintf("*** You are now level %d firemaking! ***", newLevel)) + sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d firemaking! ***", newLevel))) } } g.AccountStore.SaveCharacter(p) - msg := fmt.Sprintf("You throw %s onto the fire.", name) + msg := g.colorize(sess, "fire", fmt.Sprintf("You throw %s onto the fire.", name)) if xp > 0 && p.OptionBool("xp_drops") { - msg += fmt.Sprintf(" (+%dxp %s)", xp, player.SkillAbbr[player.Firemaking]) + msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", xp, player.SkillAbbr[player.Firemaking])) } sess.WriteLine(msg) if !p.HasItem(itemID) { - sess.WriteLine(fmt.Sprintf("You're out of %s and the fire is roaring.", name)) + sess.WriteLine(g.colorize(sess, "fire", fmt.Sprintf("You're out of %s and the fire is roaring.", g.itemColorize(sess, logDef, name)))) g.CancelAction(p) return } diff --git a/internal/game/action_cook.go b/internal/game/action_cook.go index e4d2597..a5bda41 100644 --- a/internal/game/action_cook.go +++ b/internal/game/action_cook.go @@ -4,10 +4,10 @@ import ( "fmt" "math/rand" - "thirdcollapse/internal/action" - "thirdcollapse/internal/engine" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) startCook(sess *net.Session, p *player.Player, recipe *action.RecipeDef, stationName string) { diff --git a/internal/game/action_gather.go b/internal/game/action_gather.go index e3ea105..045c51b 100644 --- a/internal/game/action_gather.go +++ b/internal/game/action_gather.go @@ -5,12 +5,13 @@ import ( "math/rand" "strings" - "thirdcollapse/internal/action" - "thirdcollapse/internal/engine" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) func (g *Game) startGather(sess *net.Session, p *player.Player, obj *object.ObjectDef, st *world.ObjState) { @@ -263,6 +264,8 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) { msg := drop.Message if msg == "" { msg = fmt.Sprintf("You manage to get some %s.", g.itemColorize(sess, itemDef, itemName)) + } else { + msg = color.ExpandTags(g.colorMode(sess), msg) } if xp > 0 && p.OptionBool("xp_drops") { msg += g.colorize(sess, "xp", fmt.Sprintf(" (+%dxp %s)", xp, player.SkillAbbr[player.SkillName(cfg.Skill)])) diff --git a/internal/game/action_room.go b/internal/game/action_room.go index 426b512..603ac7e 100644 --- a/internal/game/action_room.go +++ b/internal/game/action_room.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "thirdcollapse/internal/net" + "thehouseoficarus/internal/net" ) func (g *Game) RunEnterSteps(sess *net.Session, roomID int) { @@ -40,7 +40,7 @@ func (g *Game) BroadcastRespawns() { msg := strings.ReplaceAll(cfg.RespawnBroadcast, "{name}", name) if g.Hub != nil { for _, sess := range g.Hub.PlayersInRoom(st.RoomID) { - sess.WriteLine(fmt.Sprintf("\n%s", msg)) + sess.WriteLine(g.colorize(sess, "broadcast", fmt.Sprintf("\n%s", msg))) } } } diff --git a/internal/game/action_search.go b/internal/game/action_search.go index e9d3d46..d45066d 100644 --- a/internal/game/action_search.go +++ b/internal/game/action_search.go @@ -3,10 +3,10 @@ package game import ( "fmt" - "thirdcollapse/internal/action" - "thirdcollapse/internal/engine" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) startSearch(sess *net.Session, p *player.Player, itemID string, slotIdx int) { @@ -101,23 +101,24 @@ func (g *Game) giveSearchLoot(sess *net.Session, p *player.Player, drop *action. } name := drop.ItemID - if def, err := g.ItemStore.Load(drop.ItemID); err == nil { - name = def.Name + lootDef, _ := g.ItemStore.Load(drop.ItemID) + if lootDef != nil { + name = lootDef.Name } if drop.ItemID == "credits" { p.Credits += qty - sess.WriteLine(fmt.Sprintf("You find %d credits.", qty)) + sess.WriteLine(g.colorize(sess, "credits_pickup", fmt.Sprintf("You find %d credits.", qty))) return } freeSlot := p.FirstFreeSlot() if freeSlot == -1 { g.World.AddGroundItem(p.RoomID, drop.ItemID, qty) - sess.WriteLine(fmt.Sprintf("You find %s. It falls to the ground.", name)) + sess.WriteLine(fmt.Sprintf("You find %s. It falls to the ground.", g.itemColorize(sess, lootDef, name))) return } p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: drop.ItemID, Quantity: qty}) - sess.WriteLine(fmt.Sprintf("You find %s.", name)) + sess.WriteLine(fmt.Sprintf("You find %s.", g.itemColorize(sess, lootDef, name))) } diff --git a/internal/game/action_talk.go b/internal/game/action_talk.go index 9e90f93..30bf206 100644 --- a/internal/game/action_talk.go +++ b/internal/game/action_talk.go @@ -4,11 +4,11 @@ import ( "fmt" "strings" - "thirdcollapse/internal/action" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) func (g *Game) startMobTalk(sess *net.Session, p *player.Player, mob *world.MobInstance) { @@ -54,7 +54,7 @@ func (g *Game) startTalk(sess *net.Session, p *player.Player, obj *object.Object } func (g *Game) showTalkNode(sess *net.Session, node action.TalkNode) { - sess.WriteLine(fmt.Sprintf("\n%s", node.Message)) + sess.WriteLine(fmt.Sprintf("\n%s", g.colorize(sess, "dialog", node.Message))) if node.Action != nil { g.applyNodeAction(sess, node.Action) diff --git a/internal/game/action_toggle.go b/internal/game/action_toggle.go index f20d03c..db7a199 100644 --- a/internal/game/action_toggle.go +++ b/internal/game/action_toggle.go @@ -3,9 +3,9 @@ package game import ( "fmt" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" ) func (g *Game) startToggle(sess *net.Session, p *player.Player, obj *object.ObjectDef) { @@ -41,7 +41,7 @@ func (g *Game) startToggle(sess *net.Session, p *player.Player, obj *object.Obje if g.Hub != nil { for _, other := range g.Hub.PlayersInRoom(p.RoomID) { if other != sess && other.Player != nil { - other.WriteLine(fmt.Sprintf("\n%s uses the %s.", p.Name, obj.Name)) + other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s uses the %s.", p.Name, obj.Name))) } } } diff --git a/internal/game/action_use.go b/internal/game/action_use.go index d953521..b71ec17 100644 --- a/internal/game/action_use.go +++ b/internal/game/action_use.go @@ -4,11 +4,11 @@ import ( "fmt" "math/rand" - "thirdcollapse/internal/action" - "thirdcollapse/internal/engine" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" ) func (g *Game) startUse(sess *net.Session, p *player.Player, obj *object.ObjectDef) { @@ -99,16 +99,17 @@ func (g *Game) advanceUse(sess *net.Session, p *player.Player) { p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: cfg.Reward.ItemID, Quantity: qty}) if cfg.XP > 0 { if newLevel := p.AddSkillXP(player.SkillName(cfg.Skill), cfg.XP); newLevel > 0 { - sess.WriteLine(fmt.Sprintf("*** You are now level %d %s! ***", newLevel, cfg.Skill)) + sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d %s! ***", newLevel, cfg.Skill))) } } g.AccountStore.SaveCharacter(p) itemName := cfg.Reward.ItemID - if def, err := g.ItemStore.Load(cfg.Reward.ItemID); err == nil { - itemName = def.Name + rewardDef, _ := g.ItemStore.Load(cfg.Reward.ItemID) + if rewardDef != nil { + itemName = rewardDef.Name } - line := fmt.Sprintf("You make a %s.", itemName) + line := fmt.Sprintf("You make a %s.", g.itemColorize(sess, rewardDef, itemName)) if cfg.XP > 0 && p.OptionBool("xp_drops") { line += fmt.Sprintf(" (+%dxp %s)", cfg.XP, player.SkillAbbr[player.SkillName(cfg.Skill)]) } diff --git a/internal/game/cmd_alias.go b/internal/game/cmd_alias.go index cb0537e..e7e4e12 100644 --- a/internal/game/cmd_alias.go +++ b/internal/game/cmd_alias.go @@ -5,7 +5,7 @@ import ( "sort" "strings" - "thirdcollapse/internal/net" + "thehouseoficarus/internal/net" ) func (g *Game) doAlias(sess *net.Session, args []string) { diff --git a/internal/game/cmd_attack.go b/internal/game/cmd_attack.go index 2e15419..359e0ac 100644 --- a/internal/game/cmd_attack.go +++ b/internal/game/cmd_attack.go @@ -6,12 +6,12 @@ import ( "strconv" "strings" - "thirdcollapse/internal/combat" - "thirdcollapse/internal/engine" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) func (g *Game) doAttack(sess *net.Session, input string) { @@ -308,7 +308,7 @@ func (g *Game) endCombat(sess *net.Session, p *player.Player, mob *world.MobInst op := other.Player.(*player.Player) mobLvl := mobCombatLevel(mob) levelStr := g.levelColorize(other, op.CombatLevel(), mobLvl, fmt.Sprintf("(level %d)", mobLvl)) - other.WriteLine(fmt.Sprintf("\n%s has slain %s %s!", p.Name, mobDisplayName(mob, false), levelStr)) + other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\n%s has slain %s %s!", p.Name, mobDisplayName(mob, false), levelStr))) } } } @@ -324,7 +324,7 @@ func (g *Game) endCombat(sess *net.Session, p *player.Player, mob *world.MobInst if !mob.Unique { dropper = "The " + mob.Name } - sess.WriteLine(fmt.Sprintf(" %s drops: %s", dropper, name)) + sess.WriteLine(g.colorize(sess, "drop_message", fmt.Sprintf(" %s drops: %s", dropper, name))) } if len(mob.Drops.Loot) > 0 { @@ -345,9 +345,9 @@ func (g *Game) endCombat(sess *net.Session, p *player.Player, mob *world.MobInst dropper = "The " + mob.Name } if qty > 1 { - sess.WriteLine(fmt.Sprintf(" %s drops: %d x %s", dropper, qty, name)) + sess.WriteLine(g.colorize(sess, "drop_message", fmt.Sprintf(" %s drops: %d x %s", dropper, qty, name))) } else { - sess.WriteLine(fmt.Sprintf(" %s drops: %s", dropper, name)) + sess.WriteLine(g.colorize(sess, "drop_message", fmt.Sprintf(" %s drops: %s", dropper, name))) } } } @@ -476,7 +476,7 @@ func (g *Game) respawnMob(instanceID string) { if p, ok := sess.Player.(*player.Player); ok && p.OptionBool("mob_spawn") { mobLvl := mobCombatLevel(inst) levelStr := g.levelColorize(sess, p.CombatLevel(), mobLvl, fmt.Sprintf("(level %d)", mobLvl)) - sess.WriteLine(fmt.Sprintf("\n%s %s spawns in the area.", mobDisplayName(inst, false), levelStr)) + sess.WriteLine(g.colorize(sess, "broadcast", fmt.Sprintf("\n%s %s spawns in the area.", mobDisplayName(inst, false), levelStr))) } } } diff --git a/internal/game/cmd_color.go b/internal/game/cmd_color.go index 7a208a2..1dd14fa 100644 --- a/internal/game/cmd_color.go +++ b/internal/game/cmd_color.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "thirdcollapse/internal/color" - "thirdcollapse/internal/config" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/config" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doColor(sess *net.Session, input string) { @@ -57,8 +57,8 @@ func (g *Game) doColor(sess *net.Session, input string) { spec := color.Parse(value) if spec.Empty() && value != "" { sess.WriteLine(fmt.Sprintf("\nInvalid color string: %s", value)) - sess.WriteLine("Format: fg=<color> bg=<color> bold dim italic underline") - sess.WriteLine("Example: fg=green bg=black bold") + sess.WriteLine("Format: <0-255> [bg:<0-255>] [bold] [dim] [underline]") + sess.WriteLine("Example: 208 bold") return } @@ -150,17 +150,40 @@ var colorCategoryOrder = []string{ "victory", "miss", "error", + "say", + "dialog", + "broadcast", + "fire", + "eat_food", + "drop_message", + "credits_pickup", } func showColorTable(g *Game, sess *net.Session) { p := sess.Player.(*player.Player) + mode := g.colorMode(sess) table := &Table{ - Columns: []string{"Target", "Color", "Source"}, + Columns: []string{ + color.Render(mode, color.Parse("75"), "Target"), + "Color", + color.Render(mode, color.Parse("243"), "Source"), + }, } for _, cat := range colorCategoryOrder { val := g.getCurrentColor(sess, cat) source := g.colorSource(sess, cat) - table.Rows = append(table.Rows, []string{cat, val, source}) + coloredVal := val + if val != "off" && val != "" { + spec := color.Parse(val) + if !spec.Empty() { + coloredVal = color.Render(mode, spec, val) + } + } + table.Rows = append(table.Rows, []string{ + color.Render(mode, color.Parse("75"), cat), + coloredVal, + color.Render(mode, color.Parse("243"), source), + }) } for _, line := range table.Render(p.OptionBool("unicode")) { sess.WriteLine(line) diff --git a/internal/game/cmd_colortable.go b/internal/game/cmd_colortable.go new file mode 100644 index 0000000..90ae8c4 --- /dev/null +++ b/internal/game/cmd_colortable.go @@ -0,0 +1,151 @@ +package game + +import ( + "fmt" + "strings" + + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +func (g *Game) doColortable(sess *net.Session) { + mode := g.colorMode(sess) + if mode == "none" || mode == "" { + sess.WriteLine("\nEnable colors first: option color ansi (or xterm256)") + return + } + + p := sess.Player.(*player.Player) + unicode := p.OptionBool("unicode") + + sess.WriteLine(fmt.Sprintf("\nColor mode: %s", mode)) + + sess.WriteLine("") + writeSectionHeader(sess, "Foreground Colors", unicode) + writeColorBlock(sess, mode, true) + + sess.WriteLine("") + writeSectionHeader(sess, "Background Colors", unicode) + writeColorBlock(sess, mode, false) + + sess.WriteLine("") + writeSectionHeader(sess, "Combinations", unicode) + writeCombinations(sess, mode) + + sess.WriteLine("") + writeSectionHeader(sess, "ANSI Mode (16 colors)", unicode) + writeANSITable(sess, mode) +} + +func writeSectionHeader(sess *net.Session, title string, unicode bool) { + if unicode { + sess.WriteLine(fmt.Sprintf("── %s ──", title)) + } else { + sess.WriteLine(fmt.Sprintf("-- %s --", title)) + } +} + +func writeColorBlock(sess *net.Session, mode string, fg bool) { + sess.WriteLine("Standard:") + writeColorRow(sess, mode, 0, 8, fg) + sess.WriteLine("Bright:") + writeColorRow(sess, mode, 8, 16, fg) + + sess.WriteLine("Color cube:") + for blockRow := 0; blockRow < 2; blockRow++ { + for row := 0; row < 6; row++ { + var sb strings.Builder + for block := 0; block < 3; block++ { + base := 16 + (blockRow*3+block)*36 + row*6 + for col := 0; col < 6; col++ { + idx := base + col + if idx > 231 { + break + } + if fg { + sb.WriteString(color.FgCode(mode, idx)) + } else { + sb.WriteString(color.ContrastFg(mode, idx)) + sb.WriteString(color.BgCode(mode, idx)) + } + sb.WriteString(fmt.Sprintf("%4d", idx)) + sb.WriteString(color.Reset) + } + if block < 2 { + sb.WriteString(" ") + } + } + sess.WriteLine(sb.String()) + } + if blockRow == 0 { + sess.WriteLine("") + } + } + + sess.WriteLine("Grayscale:") + writeColorRow(sess, mode, 232, 244, fg) + writeColorRow(sess, mode, 244, 256, fg) +} + +func writeColorRow(sess *net.Session, mode string, start, end int, fg bool) { + var sb strings.Builder + for i := start; i < end && i < 256; i++ { + if fg { + sb.WriteString(color.FgCode(mode, i)) + } else { + sb.WriteString(color.ContrastFg(mode, i)) + sb.WriteString(color.BgCode(mode, i)) + } + sb.WriteString(fmt.Sprintf("%4d", i)) + sb.WriteString(color.Reset) + } + sess.WriteLine(sb.String()) +} + +func writeCombinations(sess *net.Session, mode string) { + type combo struct { + fg int + bg int + desc string + } + combos := []combo{ + {15, 0, "white on black"}, + {208, 0, "orange on black"}, + {0, 178, "black on gold"}, + {15, 24, "white on dark blue"}, + {0, 15, "black on white"}, + {15, 88, "white on dark red"}, + {178, 52, "gold on brown"}, + {14, 0, "bright cyan on black"}, + {0, 228, "black on pale yellow"}, + {15, 22, "white on dark green"}, + {9, 0, "bright red on black"}, + {11, 17, "bright yellow on navy"}, + } + for _, c := range combos { + spec := color.ColorSpec{Fg: c.fg, Bg: c.bg} + sample := color.Render(mode, spec, fmt.Sprintf(" %3d on %3d ", c.fg, c.bg)) + sess.WriteLine(fmt.Sprintf(" %s %s", sample, c.desc)) + } +} + +func writeANSITable(sess *net.Session, mode string) { + names := [16]string{ + "black", "red", "green", "yellow", + "blue", "magenta", "cyan", "white", + "brt black", "brt red", "brt green", "brt yellow", + "brt blue", "brt magenta", "brt cyan", "brt white", + } + for row := 0; row < 2; row++ { + var sb strings.Builder + for col := 0; col < 8; col++ { + idx := row*8 + col + sb.WriteString(color.FgCode(mode, idx)) + sb.WriteString(fmt.Sprintf("%3d", idx)) + sb.WriteString(color.Reset) + sb.WriteString(fmt.Sprintf(" %-12s", names[idx])) + } + sess.WriteLine(sb.String()) + } +} diff --git a/internal/game/cmd_cook.go b/internal/game/cmd_cook.go index be5fa3a..054a4b0 100644 --- a/internal/game/cmd_cook.go +++ b/internal/game/cmd_cook.go @@ -5,9 +5,9 @@ import ( "strconv" "strings" - "thirdcollapse/internal/action" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) type cookableEntry struct { diff --git a/internal/game/cmd_description.go b/internal/game/cmd_description.go index 56ce441..20ada37 100644 --- a/internal/game/cmd_description.go +++ b/internal/game/cmd_description.go @@ -3,8 +3,8 @@ package game import ( "fmt" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doDescription(sess *net.Session) { diff --git a/internal/game/cmd_drop.go b/internal/game/cmd_drop.go index b08eadf..702d1db 100644 --- a/internal/game/cmd_drop.go +++ b/internal/game/cmd_drop.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doDropAll(sess *net.Session) { @@ -179,11 +179,11 @@ func (g *Game) doDrop(sess *net.Session, input string) { dropped := qty - remaining g.AccountStore.SaveCharacter(p) if dropped == 0 { - sess.WriteLine(fmt.Sprintf("You don't have any %s.", name)) + sess.WriteLine(fmt.Sprintf("You don't have any %s.", g.itemColorize(sess, def, name))) } else if dropped == 1 { - sess.WriteLine(fmt.Sprintf("You drop a %s.", name)) + sess.WriteLine(fmt.Sprintf("You drop a %s.", g.itemColorize(sess, def, name))) } else { - sess.WriteLine(fmt.Sprintf("You drop %d x %s.", dropped, name)) + sess.WriteLine(fmt.Sprintf("You drop %d x %s.", dropped, g.itemColorize(sess, def, name))) } } @@ -215,7 +215,7 @@ func (g *Game) handleDropAllConfirm(sess *net.Session, input string) { if def != nil { name = def.Name } - dropped = append(dropped, name) + dropped = append(dropped, g.itemColorize(sess, def, name)) p.SetInvSlot(i, nil) } diff --git a/internal/game/cmd_eat.go b/internal/game/cmd_eat.go index 5d6292b..9d4bfd0 100644 --- a/internal/game/cmd_eat.go +++ b/internal/game/cmd_eat.go @@ -4,9 +4,9 @@ import ( "fmt" "time" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" ) func (g *Game) doEat(sess *net.Session, input string) { @@ -65,7 +65,7 @@ func (g *Game) doEat(sess *net.Session, input string) { } if !p.OptionBool("queue_silently") { - sess.WriteLine(fmt.Sprintf("\nYou prepare to eat %s.", def.Name)) + sess.WriteLine(fmt.Sprintf("\nYou prepare to eat %s.", g.itemColorize(sess, def, def.Name))) } } @@ -98,7 +98,7 @@ func (g *Game) doEatNow(sess *net.Session, p *player.Player, itemID string, def p.ActionState = &ActionState{Type: ActionEating, TargetName: def.Name} - sess.WriteLine(def.EatMessage) + sess.WriteLine(g.colorize(sess, "eat_food", def.EatMessage)) if p.HP <= 0 { g.endCombat(sess, p, nil) diff --git a/internal/game/cmd_equipment.go b/internal/game/cmd_equipment.go index 4947d2b..7113880 100644 --- a/internal/game/cmd_equipment.go +++ b/internal/game/cmd_equipment.go @@ -3,8 +3,8 @@ package game import ( "fmt" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doEquipment(sess *net.Session) { diff --git a/internal/game/cmd_get.go b/internal/game/cmd_get.go index e75c59e..8237ec9 100644 --- a/internal/game/cmd_get.go +++ b/internal/game/cmd_get.go @@ -3,8 +3,8 @@ package game import ( "fmt" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doGet(sess *net.Session, input string) { @@ -125,17 +125,9 @@ func (g *Game) doGet(sess *net.Session, input string) { if picked == 0 { sess.WriteLine("Your inventory is full.") } else if picked == 1 { - name := itemID - if def != nil { - name = def.Name - } - sess.WriteLine(fmt.Sprintf("You pick up a %s.", name)) + sess.WriteLine(fmt.Sprintf("You pick up a %s.", g.itemColorize(sess, def, name))) } else { - name := itemID - if def != nil { - name = def.Name - } - sess.WriteLine(fmt.Sprintf("You pick up %d x %s.", picked, name)) + sess.WriteLine(fmt.Sprintf("You pick up %d x %s.", picked, g.itemColorize(sess, def, name))) } } @@ -306,7 +298,7 @@ func (g *Game) doGetAll(sess *net.Session) { break } slot.Quantity += qty - picked = append(picked, fmt.Sprintf("%s (now %d)", def.Name, slot.Quantity)) + picked = append(picked, fmt.Sprintf("%s (now %d)", g.itemColorize(sess, def, def.Name), slot.Quantity)) stacked = true qty = 0 break @@ -335,7 +327,7 @@ func (g *Game) doGetAll(sess *net.Session) { if def != nil { name = def.Name } - picked = append(picked, name) + picked = append(picked, g.itemColorize(sess, def, name)) break } @@ -361,7 +353,7 @@ func (g *Game) doGetAll(sess *net.Session) { if def != nil { name = def.Name } - picked = append(picked, name) + picked = append(picked, g.itemColorize(sess, def, name)) qty -= take } } @@ -388,9 +380,9 @@ func (g *Game) pickupCredits(sess *net.Session, p *player.Player, itemID string) p.Credits += qty g.AccountStore.SaveCharacter(p) if qty == 1 { - sess.WriteLine("You pick up 1 credit.") + sess.WriteLine(g.colorize(sess, "credits_pickup", "You pick up 1 credit.")) } else { - sess.WriteLine(fmt.Sprintf("You pick up %d credits. (total: %d)", qty, p.Credits)) + sess.WriteLine(g.colorize(sess, "credits_pickup", fmt.Sprintf("You pick up %d credits. (total: %d)", qty, p.Credits))) } } diff --git a/internal/game/cmd_inventory.go b/internal/game/cmd_inventory.go index 7017b18..8684fcd 100644 --- a/internal/game/cmd_inventory.go +++ b/internal/game/cmd_inventory.go @@ -3,21 +3,19 @@ package game import ( "fmt" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doInventory(sess *net.Session) { p := sess.Player.(*player.Player) sess.WriteLine("") - sess.WriteLine("Inventory (28 slots):") + sess.WriteLine(fmt.Sprintf("Inventory (%d free):", p.FreeSlots())) - empty := 0 num := 0 for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot == nil { - empty++ continue } num++ @@ -33,5 +31,4 @@ func (g *Game) doInventory(sess *net.Session) { sess.WriteLine(fmt.Sprintf(" %2d) %s", num, coloredName)) } } - sess.WriteLine(fmt.Sprintf(" (%d empty slots)", empty)) } diff --git a/internal/game/cmd_look.go b/internal/game/cmd_look.go index bd738be..8cadfd3 100644 --- a/internal/game/cmd_look.go +++ b/internal/game/cmd_look.go @@ -2,16 +2,16 @@ package game import ( "fmt" - "regexp" "sort" "strconv" "strings" - "thirdcollapse/internal/combat" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) func (g *Game) doLook(sess *net.Session) { @@ -32,9 +32,11 @@ func (g *Game) doLook(sess *net.Session) { descWidth = 70 } rawLines := wrapText(room.Description, descWidth) + mode := g.colorMode(sess) + roomDescSpec := g.resolveColor(sess, "room_desc") descLines := make([]string, len(rawLines)) for i, l := range rawLines { - descLines[i] = g.colorize(sess, "room_desc", l) + descLines[i] = color.ExpandTagsWithDefault(mode, roomDescSpec, l) } wroteDesc := false if p.OptionString("tiny_map") != "off" { @@ -331,7 +333,7 @@ func (g *Game) doLook(sess *net.Session) { for _, other := range others { if other != sess && other.Player != nil { op := other.Player.(*player.Player) - line := fmt.Sprintf("\n%s is here", op.Name) + line := fmt.Sprintf("\n%s is here", g.colorize(sess, "player_name", op.Name)) if cs := combat.GetCombat(op.Name); cs != nil { if mob := g.MobStore.GetInstance(cs.MobID); mob != nil && mob.HP > 0 { name := mobDisplayName(mob, false) @@ -476,7 +478,7 @@ func (g *Game) doLookTarget(sess *net.Session, input string) { } lines := []string{ "", - def.Name, + g.itemColorize(sess, def, def.Name), fmt.Sprintf(" %s", def.Description), fmt.Sprintf(" Value: %d credits", def.Value), } @@ -606,10 +608,8 @@ 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, "")) + return color.VisibleLen(s) } func wrapText(text string, width int) []string { diff --git a/internal/game/cmd_map.go b/internal/game/cmd_map.go index f4ec11c..1c78be3 100644 --- a/internal/game/cmd_map.go +++ b/internal/game/cmd_map.go @@ -1,8 +1,8 @@ package game import ( - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doMap(sess *net.Session) { diff --git a/internal/game/cmd_move.go b/internal/game/cmd_move.go index 11aa17e..a140003 100644 --- a/internal/game/cmd_move.go +++ b/internal/game/cmd_move.go @@ -3,9 +3,9 @@ package game import ( "fmt" - "thirdcollapse/internal/combat" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doMove(sess *net.Session, dir string) { diff --git a/internal/game/cmd_option.go b/internal/game/cmd_option.go index 9302baa..3d4d420 100644 --- a/internal/game/cmd_option.go +++ b/internal/game/cmd_option.go @@ -5,22 +5,34 @@ import ( "strconv" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doOption(sess *net.Session, input string) { p := sess.Player.(*player.Player) + mode := g.colorMode(sess) if input == "" { sess.WriteLine("") table := &Table{ - Columns: []string{"Option", "Value", "Valid", "Description"}, + Columns: []string{ + color.Render(mode, color.Parse("75"), "Option"), + color.Render(mode, color.Parse("230"), "Value"), + color.Render(mode, color.Parse("243"), "Valid"), + color.Render(mode, color.Parse("252"), "Description"), + }, } for _, def := range player.OptionDefs { val := formatOptionValue(p, &def) valid := formatValidValues(&def) - table.Rows = append(table.Rows, []string{def.Name, val, valid, def.Description}) + table.Rows = append(table.Rows, []string{ + color.Render(mode, color.Parse("75"), def.Name), + color.Render(mode, color.Parse("230"), val), + color.Render(mode, color.Parse("243"), valid), + color.Render(mode, color.Parse("252"), def.Description), + }) } for _, line := range table.Render(p.OptionBool("unicode")) { sess.WriteLine(line) @@ -56,7 +68,20 @@ func (g *Game) doOption(sess *net.Session, input string) { p.Options = make(map[string]any) } p.Options[def.Name] = parsed - g.AccountStore.SaveCharacter(p) + + if sess.Account.Options == nil { + sess.Account.Options = make(map[string]any) + } + sess.Account.Options[def.Name] = parsed + + acc, err := g.AccountStore.LoadAccount(sess.Account.Name) + if err == nil { + if acc.Options == nil { + acc.Options = make(map[string]any) + } + acc.Options[def.Name] = parsed + g.AccountStore.SaveAccount(acc) + } sess.WriteLine(fmt.Sprintf("\n%s set to %s.", def.Name, formatOptionValue(p, def))) } diff --git a/internal/game/cmd_prompt.go b/internal/game/cmd_prompt.go new file mode 100644 index 0000000..81c3409 --- /dev/null +++ b/internal/game/cmd_prompt.go @@ -0,0 +1,33 @@ +package game + +import ( + "fmt" + + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" +) + +func (g *Game) doPrompt(sess *net.Session, input string) { + p := sess.Player.(*player.Player) + + if input == "" { + prompt := p.Prompt + if prompt == "" { + prompt = "> " + } + sess.WriteLine(fmt.Sprintf("\nPrompt: %s", prompt)) + sess.WriteLine("Use 'prompt <value>' to change. Use 'prompt reset' to restore default.") + return + } + + if input == "reset" { + p.Prompt = "> " + g.AccountStore.SaveCharacter(p) + sess.WriteLine("\nPrompt reset to default.") + return + } + + p.Prompt = input + g.AccountStore.SaveCharacter(p) + sess.WriteLine(fmt.Sprintf("\nPrompt set to: %s", input)) +} diff --git a/internal/game/cmd_queued.go b/internal/game/cmd_queued.go index 040ff31..d9e92d5 100644 --- a/internal/game/cmd_queued.go +++ b/internal/game/cmd_queued.go @@ -4,8 +4,8 @@ import ( "fmt" "sort" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doQueued(sess *net.Session) { diff --git a/internal/game/cmd_quit.go b/internal/game/cmd_quit.go index 446305b..89b6e7f 100644 --- a/internal/game/cmd_quit.go +++ b/internal/game/cmd_quit.go @@ -1,10 +1,10 @@ package game import ( - "thirdcollapse/internal/combat" - "thirdcollapse/internal/engine" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doQuit(sess *net.Session) { diff --git a/internal/game/cmd_remove.go b/internal/game/cmd_remove.go index f511ec4..61fdc29 100644 --- a/internal/game/cmd_remove.go +++ b/internal/game/cmd_remove.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" ) func (g *Game) doRemove(sess *net.Session, input string) { @@ -68,8 +68,9 @@ func (g *Game) doRemove(sess *net.Session, input string) { g.AccountStore.SaveCharacter(p) name := foundItemID - if def, err := g.ItemStore.Load(foundItemID); err == nil { + def, _ := g.ItemStore.Load(foundItemID) + if def != nil { name = def.Name } - sess.WriteLine(fmt.Sprintf("You remove %s.", name)) + sess.WriteLine(fmt.Sprintf("You remove %s.", g.itemColorize(sess, def, name))) } diff --git a/internal/game/cmd_say.go b/internal/game/cmd_say.go index 35ec5fa..67d8165 100644 --- a/internal/game/cmd_say.go +++ b/internal/game/cmd_say.go @@ -3,8 +3,8 @@ package game import ( "fmt" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doSay(sess *net.Session, msg string) { @@ -13,9 +13,9 @@ func (g *Game) doSay(sess *net.Session, msg string) { for _, other := range g.Hub.PlayersInRoom(roomID) { if other == sess { - other.WriteLine(fmt.Sprintf("You say: %s", msg)) + other.WriteLine(fmt.Sprintf("You say: %s", g.colorize(sess, "say", msg))) } else { - other.WriteLine(fmt.Sprintf("\n%s says: %s", p.Name, msg)) + other.WriteLine(fmt.Sprintf("\n%s says: %s", g.colorize(other, "player_name", p.Name), g.colorize(other, "say", msg))) } } } diff --git a/internal/game/cmd_score.go b/internal/game/cmd_score.go index 2c62839..40d3007 100644 --- a/internal/game/cmd_score.go +++ b/internal/game/cmd_score.go @@ -4,29 +4,35 @@ import ( "fmt" "strconv" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doScore(sess *net.Session) { p := sess.Player.(*player.Player) + mode := g.colorMode(sess) sess.WriteLines( "", - fmt.Sprintf("Name: %s", p.Name), - fmt.Sprintf("Combat Level: %d", p.CombatLevel()), - fmt.Sprintf("HP: %d/%d", p.HP, p.MaxHP()), - fmt.Sprintf("Credits: %d", p.Credits), + fmt.Sprintf("Name: %s", g.colorize(sess, "player_name", p.Name)), + fmt.Sprintf("Combat Level: %s", color.Render(mode, color.Parse("230"), fmt.Sprint(p.CombatLevel()))), + fmt.Sprintf("HP: %s/%s", g.colorize(sess, "character_hp", fmt.Sprint(p.HP)), color.Render(mode, color.Parse("230"), fmt.Sprint(p.MaxHP()))), + fmt.Sprintf("Credits: %s", g.colorize(sess, "credits_pickup", fmt.Sprint(p.Credits))), ) - t := &Table{Title: "Skills", Columns: []string{"Skill", "Level", "XP"}} + t := &Table{Title: "Skills", Columns: []string{ + color.Render(mode, color.Parse("75"), "Skill"), + color.Render(mode, color.Parse("230"), "Level"), + color.Render(mode, color.Parse("222"), "XP"), + }} for _, s := range player.AllSkills { level := p.Level(s) xp := p.Skills[s] next := player.XPForNextLevel(xp) t.Rows = append(t.Rows, []string{ - string(s), - strconv.Itoa(level), - fmt.Sprintf("%d / %d XP", xp, xp+next), + color.Render(mode, color.Parse("75"), string(s)), + color.Render(mode, color.Parse("230"), strconv.Itoa(level)), + color.Render(mode, color.Parse("222"), fmt.Sprintf("%d / %d XP", xp, xp+next)), }) } for _, line := range t.Render(p.OptionBool("unicode")) { diff --git a/internal/game/cmd_search.go b/internal/game/cmd_search.go index d9a5b1e..d4d2e72 100644 --- a/internal/game/cmd_search.go +++ b/internal/game/cmd_search.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doSearch(sess *net.Session, input string) { diff --git a/internal/game/cmd_style.go b/internal/game/cmd_style.go index d86920c..901d02d 100644 --- a/internal/game/cmd_style.go +++ b/internal/game/cmd_style.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) doStyle(sess *net.Session, input string) { diff --git a/internal/game/cmd_use.go b/internal/game/cmd_use.go index ef36e12..56aa5a1 100644 --- a/internal/game/cmd_use.go +++ b/internal/game/cmd_use.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" ) func (g *Game) doUse(sess *net.Session, input string) { diff --git a/internal/game/cmd_walk.go b/internal/game/cmd_walk.go index 31f9e18..4ff6187 100644 --- a/internal/game/cmd_walk.go +++ b/internal/game/cmd_walk.go @@ -5,9 +5,9 @@ import ( "strconv" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) func (g *Game) doWalk(sess *net.Session, args []string) { diff --git a/internal/game/cmd_wear.go b/internal/game/cmd_wear.go index 7c3515f..cbc2ff3 100644 --- a/internal/game/cmd_wear.go +++ b/internal/game/cmd_wear.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" ) func (g *Game) doWear(sess *net.Session, input string) { @@ -39,7 +39,7 @@ func (g *Game) doWear(sess *net.Session, input string) { def, err := g.ItemStore.Load(slot.ItemID) if err != nil || def.EquipSlot == "" { - sess.WriteLine(fmt.Sprintf("You can't wear %s.", match.Name)) + sess.WriteLine(fmt.Sprintf("You can't wear %s.", g.itemColorize(sess, def, match.Name))) return } @@ -76,7 +76,7 @@ func (g *Game) doWear(sess *net.Session, input string) { if def.WeaponType != "" { verb = "wield" } - sess.WriteLine(fmt.Sprintf("You %s %s.", verb, match.Name)) + sess.WriteLine(fmt.Sprintf("You %s %s.", verb, g.itemColorize(sess, def, match.Name))) } func (g *Game) doWearAll(sess *net.Session) { @@ -147,7 +147,7 @@ func (g *Game) doWearAll(sess *net.Session) { if def != nil { name = def.Name } - equipped = append(equipped, name) + equipped = append(equipped, g.itemColorize(sess, def, name)) } if len(equipped) == 0 { diff --git a/internal/game/color.go b/internal/game/color.go index f93f097..4fd8000 100644 --- a/internal/game/color.go +++ b/internal/game/color.go @@ -1,11 +1,11 @@ package game import ( - "thirdcollapse/internal/color" - "thirdcollapse/internal/config" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/config" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" ) func (g *Game) colorMode(sess *net.Session) string { @@ -19,7 +19,7 @@ func (g *Game) resolveColor(sess *net.Session, category string) color.ColorSpec if sess.Account != nil && sess.Account.Colors != nil { if val, ok := sess.Account.Colors[category]; ok { if val == "off" { - return color.ColorSpec{} + return color.NoColor() } if val != "" { return color.Parse(val) @@ -34,7 +34,7 @@ func (g *Game) resolveColor(sess *net.Session, category string) color.ColorSpec if val, ok := config.DefaultColors()[category]; ok && val != "" { return color.Parse(val) } - return color.ColorSpec{} + return color.NoColor() } func (g *Game) colorize(sess *net.Session, category, text string) string { @@ -46,15 +46,15 @@ func levelColorSpec(myLevel, theirLevel int) color.ColorSpec { diff := theirLevel - myLevel switch { case diff == 0: - return color.Parse("fg=white") + return color.Parse("7") case diff > 0 && diff < 5: - return color.Parse("fg=bright_yellow") + return color.Parse("11") case diff >= 5: - return color.Parse("fg=bright_red") + return color.Parse("9") case diff < 0 && diff > -5: - return color.Parse("fg=yellow") + return color.Parse("3") default: - return color.Parse("fg=green") + return color.Parse("2") } } diff --git a/internal/game/game.go b/internal/game/game.go index 33dd0d1..9d07ead 100644 --- a/internal/game/game.go +++ b/internal/game/game.go @@ -7,14 +7,14 @@ import ( "sync" "time" - "thirdcollapse/internal/action" - "thirdcollapse/internal/combat" - "thirdcollapse/internal/config" - "thirdcollapse/internal/engine" - "thirdcollapse/internal/net" - "thirdcollapse/internal/object" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/action" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/config" + "thehouseoficarus/internal/engine" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/object" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) type CommandClass int @@ -124,6 +124,8 @@ func (g *Game) HandleSession(sess *net.Session, input string) { g.handleDropAllConfirm(sess, input) case net.StateCookRecipe: g.handleCookRecipe(sess, input) + case net.StateColorChoice: + g.handleColorChoice(sess, input) } } @@ -132,9 +134,10 @@ func classifyCommand(cmd string) CommandClass { case "say", "score", "sc", "inventory", "i", "inv", "equipment", "eq", "look", "l", "exits", "help", "map", "option", "options", "alias", "unalias", - "description", "desc", "queued", "color", "colors": + "description", "desc", "queued", "color", "colors", + "colortable", "prompt", "style": return ClassInstant - case "wear", "wield", "remove", "unwear", "unwield", "style": + case "wear", "wield", "remove", "unwear", "unwield": return ClassFree case "get", "take", "grab", "pick", "drop", "attack", "kill", @@ -310,6 +313,19 @@ func (g *Game) executeCommand(sess *net.Session, cmd string, args []string, rawI g.doOption(sess, strings.Join(args, " ")) case "color", "colors": g.doColor(sess, strings.Join(args, " ")) + case "colortable": + g.doColortable(sess) + case "prompt": + if len(args) == 0 { + g.doPrompt(sess, "") + } else { + msgStart := strings.Index(strings.ToLower(rawInput), "prompt ") + 7 + if msgStart >= 7 && msgStart < len(rawInput) { + g.doPrompt(sess, rawInput[msgStart:]) + } else { + g.doPrompt(sess, strings.Join(args, " ")) + } + } case "exits": g.doExits(sess) case "map": diff --git a/internal/game/help.go b/internal/game/help.go index d3f34c3..f9d822c 100644 --- a/internal/game/help.go +++ b/internal/game/help.go @@ -6,8 +6,8 @@ import ( "path/filepath" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" "gopkg.in/yaml.v3" ) @@ -29,6 +29,7 @@ var commandList = []cmdEntry{ {"burn", "Active", "Start a fire"}, {"chop / cut", "Active", "Chop trees (Woodcutting)"}, {"color / colors", "Instant", "Customize display colors"}, + {"colortable", "Instant", "Display color reference chart"}, {"cook", "Active", "Cook raw food on a fire or range"}, {"description / desc", "Instant", "Set your character description"}, {"drop", "Active", "Drop items to the ground"}, @@ -44,6 +45,7 @@ var commandList = []cmdEntry{ {"mine", "Active", "Mine rocks (Mining)"}, {"north / south / east / west / up / down", "Active", "Move in a direction"}, {"option / options", "Instant", "View or change settings"}, + {"prompt", "Instant", "Set custom command prompt"}, {"pull / push", "Active", "Toggle levers and switches"}, {"queued", "Instant", "Show pending tick actions"}, {"quit", "Active", "Rest and disconnect"}, @@ -52,7 +54,7 @@ var commandList = []cmdEntry{ {"score / sc", "Instant", "View your stats and skills"}, {"search", "Active", "Search items for loot"}, {"stoke", "Active", "Add logs to a fire"}, - {"style", "Free", "Change combat style"}, + {"style", "Instant", "Change combat style"}, {"talk / speak / ask", "Active", "Talk to NPCs"}, {"unalias", "Instant", "Remove command shortcuts"}, {"use", "Active", "Use an object (crafting)"}, diff --git a/internal/game/login_account.go b/internal/game/login_account.go index 3ad1fee..176a1ee 100644 --- a/internal/game/login_account.go +++ b/internal/game/login_account.go @@ -5,8 +5,8 @@ import ( "os" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) handleAccountName(sess *net.Session, input string) { @@ -69,6 +69,7 @@ func (g *Game) handlePassword(sess *net.Session, input string) { Characters: acc.Characters, Aliases: acc.Aliases, Colors: acc.Colors, + Options: acc.Options, } if sess.Account.Aliases == nil { sess.Account.Aliases = make(map[string]string) @@ -76,6 +77,9 @@ func (g *Game) handlePassword(sess *net.Session, input string) { if sess.Account.Colors == nil { sess.Account.Colors = make(map[string]string) } + if sess.Account.Options == nil { + sess.Account.Options = make(map[string]any) + } g.showMenu(sess) } @@ -144,8 +148,27 @@ func (g *Game) handleNewAccountConfirm(sess *net.Session, input string) { Name: acc.Name, PasswordHash: acc.PasswordHash, Aliases: make(map[string]string), + Colors: make(map[string]string), + Options: make(map[string]any), } sess.PendingPass = "" + sess.State = net.StateColorChoice + sess.Write("\nShould I disable color? [y/N] ") +} + +func (g *Game) handleColorChoice(sess *net.Session, input string) { + input = strings.ToLower(strings.TrimSpace(input)) + if input == "y" || input == "yes" { + sess.Account.Options["color"] = "none" + acc, err := g.AccountStore.LoadAccount(sess.Account.Name) + if err == nil { + if acc.Options == nil { + acc.Options = make(map[string]any) + } + acc.Options["color"] = "none" + g.AccountStore.SaveAccount(acc) + } + } g.showMenu(sess) } @@ -158,7 +181,7 @@ func (g *Game) showMenu(sess *net.Session) { } if len(sess.Account.Characters) > 0 { lines = append(lines, - " (C)onnect character to TC", + " (C)onnect character to THOI", "", " (L)ist characters", " (R)ename character", @@ -171,9 +194,9 @@ func (g *Game) showMenu(sess *net.Session) { " (A)ccount rename", " (Q)uit", "", - "> ", ) sess.WriteLines(lines...) + sess.Write("> ") } func (g *Game) handleMenu(sess *net.Session, input string) { diff --git a/internal/game/login_char.go b/internal/game/login_char.go index ba034bb..82065d3 100644 --- a/internal/game/login_char.go +++ b/internal/game/login_char.go @@ -5,8 +5,8 @@ import ( "os" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) handleNewCharName(sess *net.Session, input string) { @@ -82,6 +82,13 @@ func (g *Game) connectCharacter(sess *net.Session, name string) { g.loggedInChars[name] = sess g.charsMu.Unlock() + if sess.Account != nil && sess.Account.Options != nil { + p.Options = make(map[string]any) + for k, v := range sess.Account.Options { + p.Options[k] = v + } + } + g.World.SeedGroundItems(p.RoomID) g.seedRoomMobs(p.RoomID) g.seedRoomObjects(p.RoomID) diff --git a/internal/game/map.go b/internal/game/map.go index 8b14e25..32d3d7c 100644 --- a/internal/game/map.go +++ b/internal/game/map.go @@ -3,7 +3,7 @@ package game import ( "strings" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/world" ) type mapGlyphs struct { diff --git a/internal/game/map_test.go b/internal/game/map_test.go index 8c82f4a..a695c1b 100644 --- a/internal/game/map_test.go +++ b/internal/game/map_test.go @@ -4,7 +4,7 @@ import ( "strings" "testing" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/world" ) func TestBuildTinyMap(t *testing.T) { diff --git a/internal/game/prompt.go b/internal/game/prompt.go index 2fc36ac..de88bc6 100644 --- a/internal/game/prompt.go +++ b/internal/game/prompt.go @@ -4,17 +4,17 @@ import ( "fmt" "strings" - "thirdcollapse/internal/color" - "thirdcollapse/internal/combat" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" + "thehouseoficarus/internal/color" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" ) func (g *Game) promptStr(sess *net.Session) string { prompt := "> " if p, ok := sess.Player.(*player.Player); ok && p != nil { - if custom := p.OptionString("prompt"); custom != "" { - prompt = custom + if p.Prompt != "" { + prompt = p.Prompt } } @@ -24,42 +24,7 @@ func (g *Game) promptStr(sess *net.Session) string { } func (g *Game) expandPromptColors(sess *net.Session, text string) string { - mode := g.colorMode(sess) - for { - start := strings.Index(text, "{") - if start < 0 { - break - } - - end := strings.Index(text[start+1:], "}") - if end < 0 { - break - } - end += start + 1 - specStr := text[start+1 : end] - - if specStr == "" || specStr == "/" { - break - } - - spec := color.Parse(specStr) - if spec.Empty() { - break - } - - closeStart := strings.Index(text[end+1:], "{/}") - if closeStart < 0 { - break - } - closeStart += end + 1 - closeEnd := closeStart + 3 - - inner := text[end+1 : closeStart] - colored := color.Render(mode, spec, inner) - - text = text[:start] + colored + text[closeEnd:] - } - return text + return color.ExpandTags(g.colorMode(sess), text) } func (g *Game) expandPromptVars(sess *net.Session, text string) string { diff --git a/internal/game/table.go b/internal/game/table.go index 56c0079..b96dca1 100644 --- a/internal/game/table.go +++ b/internal/game/table.go @@ -1,8 +1,9 @@ package game import ( - "fmt" "strings" + + "thehouseoficarus/internal/color" ) type tableGlyphs struct { @@ -50,15 +51,16 @@ func (t *Table) Render(unicode bool) []string { colWidths := make([]int, nCols) for i, h := range t.Columns { - colWidths[i] = len(h) + colWidths[i] = color.VisibleLen(h) } for _, row := range t.Rows { for i, cell := range row { if i >= nCols { break } - if len(cell) > colWidths[i] { - colWidths[i] = len(cell) + vl := color.VisibleLen(cell) + if vl > colWidths[i] { + colWidths[i] = vl } } } @@ -87,7 +89,11 @@ func (t *Table) Render(unicode bool) []string { if i < len(cells) { cell = cells[i] } - b.WriteString(fmt.Sprintf(" %-*s ", colWidths[i], cell)) + pad := colWidths[i] - color.VisibleLen(cell) + if pad < 0 { + pad = 0 + } + b.WriteString(" " + cell + strings.Repeat(" ", pad) + " ") } b.WriteRune(g.side) return b.String() @@ -115,7 +121,7 @@ func (t *Table) Render(unicode bool) []string { tr.WriteRune(g.side) tr.WriteString(" ") tr.WriteString(t.Title) - padding := innerWidth - 2 - len(t.Title) + padding := innerWidth - 2 - color.VisibleLen(t.Title) if padding < 0 { padding = 0 } diff --git a/internal/game/tick.go b/internal/game/tick.go index 7567ca7..ab82254 100644 --- a/internal/game/tick.go +++ b/internal/game/tick.go @@ -4,9 +4,9 @@ import ( "fmt" "math/rand" - "thirdcollapse/internal/combat" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/combat" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) func (g *Game) DisconnectTick() { @@ -127,21 +127,21 @@ func (g *Game) WanderTick() { if !ok { continue } - if p.RoomID == m.fromRoom && p.OptionBool("mob_leave") { - if m.level > 0 { - sess.WriteLine(fmt.Sprintf("\n%s (level %d) leaves.", m.name, m.level)) - } else { - sess.WriteLine(fmt.Sprintf("\n%s moves away.", m.name)) - } + if p.RoomID == m.fromRoom && p.OptionBool("mob_leave") { + if m.level > 0 { + sess.WriteLine(g.colorize(sess, "broadcast", fmt.Sprintf("\n%s (level %d) leaves.", m.name, m.level))) + } else { + sess.WriteLine(g.colorize(sess, "broadcast", fmt.Sprintf("\n%s moves away.", m.name))) } - if p.RoomID == m.toRoom && p.OptionBool("mob_enter") { - if m.level > 0 { - sess.WriteLine(fmt.Sprintf("\n%s (level %d) enters.", m.name, m.level)) - } else { - sess.WriteLine(fmt.Sprintf("\n%s drifts in.", m.name)) - } + } + if p.RoomID == m.toRoom && p.OptionBool("mob_enter") { + if m.level > 0 { + sess.WriteLine(g.colorize(sess, "broadcast", fmt.Sprintf("\n%s (level %d) enters.", m.name, m.level))) + } else { + sess.WriteLine(g.colorize(sess, "broadcast", fmt.Sprintf("\n%s drifts in.", m.name))) } } + } } } diff --git a/internal/game/types.go b/internal/game/types.go index faed9e1..d41b18a 100644 --- a/internal/game/types.go +++ b/internal/game/types.go @@ -1,6 +1,6 @@ package game -import "thirdcollapse/internal/object" +import "thehouseoficarus/internal/object" var EquipSlots = []object.EquipSlot{ object.SlotHead, object.SlotNeck, object.SlotTorso, object.SlotLegs, diff --git a/internal/game/utils.go b/internal/game/utils.go index 729ea97..2379fc2 100644 --- a/internal/game/utils.go +++ b/internal/game/utils.go @@ -6,9 +6,9 @@ import ( "strconv" "strings" - "thirdcollapse/internal/net" - "thirdcollapse/internal/player" - "thirdcollapse/internal/world" + "thehouseoficarus/internal/net" + "thehouseoficarus/internal/player" + "thehouseoficarus/internal/world" ) func plural(n int) string { |
