aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-16 15:00:46 -0400
committerhistoria <[not public]>2026-09-16 15:00:46 -0400
commita6cf03a6ae98e4fb675da25046d148e92bcf7206 (patch)
tree194499fe9e696be8c31cd915b7ad92678f9f95e0 /internal
parent9192d94dea6be5498ab080f3e2df5667af871d7a (diff)
downloadthehouseoficarus-a6cf03a6ae98e4fb675da25046d148e92bcf7206.tar.gz
chore: staticcheck and gofmt cleanups
Diffstat (limited to 'internal')
-rw-r--r--internal/admin/api_colors.go34
-rw-r--r--internal/admin/api_colors_test.go2
-rw-r--r--internal/admin/api_room_courses.go56
-rw-r--r--internal/admin/api_rooms.go2
-rw-r--r--internal/admin/undo.go67
-rw-r--r--internal/admin/xterm_to_css.go13
-rw-r--r--internal/admin/yaml_util.go17
-rw-r--r--internal/casino/slots.go9
-rw-r--r--internal/config/config.go22
-rw-r--r--internal/engine/tick.go5
-rw-r--r--internal/game/account_txn.go5
-rw-r--r--internal/game/act_burn.go2
-rw-r--r--internal/game/act_farm.go55
-rw-r--r--internal/game/act_gather.go2
-rw-r--r--internal/game/act_room.go8
-rw-r--r--internal/game/baseline_race_test.go3
-rw-r--r--internal/game/cmd_drop.go2
-rw-r--r--internal/game/cmd_estate_directory.go2
-rw-r--r--internal/game/cmd_farm.go76
-rw-r--r--internal/game/cmd_queued.go8
-rw-r--r--internal/game/cmd_remove.go2
-rw-r--r--internal/game/cmd_room_remove_test.go8
-rw-r--r--internal/game/cmd_tech.go2
-rw-r--r--internal/game/cmd_undig.go6
-rw-r--r--internal/game/cmd_use.go2
-rw-r--r--internal/game/cmd_walk.go4
-rw-r--r--internal/game/core_course.go6
-rw-r--r--internal/game/core_login_account.go2
-rw-r--r--internal/game/core_login_char.go2
-rw-r--r--internal/game/harness_test.go17
-rw-r--r--internal/game/owner.go12
-rw-r--r--internal/game/production_index.go9
-rw-r--r--internal/game/render_help.go7
-rw-r--r--internal/game/sys_assassin.go102
-rw-r--r--internal/game/sys_buff.go5
-rw-r--r--internal/player/validate.go2
-rw-r--r--internal/validate/checks.go28
37 files changed, 67 insertions, 539 deletions
diff --git a/internal/admin/api_colors.go b/internal/admin/api_colors.go
index 876656d..b57ad82 100644
--- a/internal/admin/api_colors.go
+++ b/internal/admin/api_colors.go
@@ -3,7 +3,6 @@ package admin
import (
"net/http"
"os"
- "sort"
"strings"
"gopkg.in/yaml.v3"
@@ -185,11 +184,6 @@ func knownConstantColor(name string) bool {
return false
}
-// knownColor returns true if name is editable in either section.
-func knownColor(name string) bool {
- return knownPlayerColor(name) || knownConstantColor(name)
-}
-
// seg is a small constructor helper for the inline preview builder.
func seg(t, cat string) colorSegment {
return colorSegment{T: t, C: cat}
@@ -280,7 +274,6 @@ func buildPreview() []colorSection {
ln(seg("Oh dear, you are dead!", "death")),
txt(""),
txt(""),
-
},
})
@@ -479,20 +472,20 @@ func (s *AdminServer) getColors(w http.ResponseWriter, _ *http.Request) {
}
writeJSON(w, map[string]any{
- "colors": live,
- "default_colors": def,
- "categories": colorCategories,
- "constant_colors": liveConst,
+ "colors": live,
+ "default_colors": def,
+ "categories": colorCategories,
+ "constant_colors": liveConst,
"default_constant_colors": defConst,
- "constant_categories": constantCategories,
- "preview": buildPreview(),
+ "constant_categories": constantCategories,
+ "preview": buildPreview(),
})
}
func (s *AdminServer) saveColors(w http.ResponseWriter, r *http.Request) {
var body struct {
- Colors map[string]string `json:"colors"`
- ConstantColors map[string]string `json:"constant_colors"`
+ Colors map[string]string `json:"colors"`
+ ConstantColors map[string]string `json:"constant_colors"`
}
if err := readJSON(r, &body); err != nil {
writeJSONError(w, "invalid request body", http.StatusBadRequest)
@@ -593,14 +586,3 @@ func (s *AdminServer) writeConfigFile() error {
}
return os.WriteFile(s.configPath, out, 0644)
}
-
-// sortedKeys is a small helper retained in case future code wants a stable
-// iteration order over the category names.
-func sortedKeys(m map[string]string) []string {
- keys := make([]string, 0, len(m))
- for k := range m {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- return keys
-}
diff --git a/internal/admin/api_colors_test.go b/internal/admin/api_colors_test.go
index 1f5d3eb..53c68b1 100644
--- a/internal/admin/api_colors_test.go
+++ b/internal/admin/api_colors_test.go
@@ -225,4 +225,4 @@ func TestColorsPreviewExhaustive(t *testing.T) {
}
}
_ = io.Discard
-} \ No newline at end of file
+}
diff --git a/internal/admin/api_room_courses.go b/internal/admin/api_room_courses.go
index 47e4d8c..b8f334e 100644
--- a/internal/admin/api_room_courses.go
+++ b/internal/admin/api_room_courses.go
@@ -105,19 +105,6 @@ func toInt(v any) int {
return 0
}
-// toFloat64 coerces yaml-decoded numbers to float64.
-func toFloat64(v any) float64 {
- switch x := v.(type) {
- case int:
- return float64(x)
- case int64:
- return float64(x)
- case float64:
- return x
- }
- return 0
-}
-
// indexOfObstacleForRoom returns the index of the obstacle whose room_id
// matches roomID, or -1 if none.
func indexOfObstacleForRoom(obstacles []map[string]any, roomID int) int {
@@ -515,39 +502,6 @@ func (s *AdminServer) loadRoomMapByID(roomID int) (map[string]any, []byte, strin
return m, data, path, nil
}
-// saveRoomMapByID writes a room map back to disk and pushes an undo entry.
-func (s *AdminServer) saveRoomMapByID(roomID int, m map[string]any, desc string) error {
- path, ok := s.world.GetRoomPath(roomID)
- if !ok {
- return fmt.Errorf("room %d not found", roomID)
- }
- oldContent, _ := snapshotFile(path)
- newContent, err := writeMapAsYAML(path, m)
- if err != nil {
- return err
- }
- s.undoStack.Push(ChangeDesc{
- Description: desc,
- FilePath: path,
- OldContent: oldContent,
- NewContent: newContent,
- })
- return nil
-}
-
-// removeExit deletes an exit by direction. Returns true if it existed.
-func removeExit(m map[string]any, dir string) bool {
- ex, ok := m["exits"].(map[string]any)
- if !ok {
- return false
- }
- if _, exists := ex[dir]; exists {
- delete(ex, dir)
- return true
- }
- return false
-}
-
// exitTargetRoom returns the target room ID of an exit value (scalar or map).
func exitTargetRoom(v any) int {
switch x := v.(type) {
@@ -563,16 +517,6 @@ func exitTargetRoom(v any) int {
return 0
}
-// exitIsAlwaysBlocked returns whether an exit value carries always_blocked: true.
-func exitIsAlwaysBlocked(v any) bool {
- if mm, ok := v.(map[string]any); ok {
- if h, ok := mm["always_blocked"]; ok {
- return h == true
- }
- }
- return false
-}
-
// isHorizontalDir returns whether d is one of the 8 horizontal directions.
func isHorizontalDir(d string) bool {
for _, h := range horizontalExitDirs {
diff --git a/internal/admin/api_rooms.go b/internal/admin/api_rooms.go
index 01b7b9a..8ee16a5 100644
--- a/internal/admin/api_rooms.go
+++ b/internal/admin/api_rooms.go
@@ -131,7 +131,7 @@ func (s *AdminServer) handleRooms(w http.ResponseWriter, r *http.Request) {
conflicts++
})
if conflicts > 0 {
- writeJSON(w, map[string]any{"error": fmt.Sprintf("Add Next Step: that grid spot is already occupied — room would cause map overlap")})
+ writeJSON(w, map[string]any{"error": "Add Next Step: that grid spot is already occupied — room would cause map overlap"})
return
}
} else {
diff --git a/internal/admin/undo.go b/internal/admin/undo.go
index 98d8d9a..51610f8 100644
--- a/internal/admin/undo.go
+++ b/internal/admin/undo.go
@@ -414,73 +414,6 @@ func (us *UndoStack) save() {
}
}
-func (us *UndoStack) load() {
- data, err := os.ReadFile(us.filePath)
- if err != nil {
- return
- }
- log.Printf("undo: loaded history from %s", us.filePath)
- type extraEntry struct {
- FilePath string `json:"file_path"`
- NewFilePath string `json:"new_file_path,omitempty"`
- OldContent string `json:"old_content"`
- NewContent string `json:"new_content"`
- IsDelete bool `json:"is_delete,omitempty"`
- IsCreate bool `json:"is_create,omitempty"`
- }
- type entry struct {
- Time string `json:"time"`
- Description string `json:"description"`
- FilePath string `json:"file_path"`
- NewFilePath string `json:"new_file_path,omitempty"`
- OldContent string `json:"old_content"`
- NewContent string `json:"new_content"`
- IsDelete bool `json:"is_delete"`
- IsCreate bool `json:"is_create"`
- ExtraFiles []extraEntry `json:"extra_files,omitempty"`
- IDMap map[int]int `json:"id_map,omitempty"`
- }
- var saveData struct {
- History []entry `json:"history"`
- Redo []entry `json:"redo"`
- }
- if err := json.Unmarshal(data, &saveData); err != nil {
- log.Printf("undo: failed to unmarshal history: %v", err)
- return
- }
- fromEntries := func(entries []entry) []ChangeDesc {
- var changes []ChangeDesc
- for _, e := range entries {
- var extra []ExtraFile
- for _, ef := range e.ExtraFiles {
- extra = append(extra, ExtraFile{
- FilePath: ef.FilePath,
- NewFilePath: ef.NewFilePath,
- OldContent: []byte(ef.OldContent),
- NewContent: []byte(ef.NewContent),
- IsDelete: ef.IsDelete,
- IsCreate: ef.IsCreate,
- })
- }
- changes = append(changes, ChangeDesc{
- Time: e.Time,
- Description: e.Description,
- FilePath: e.FilePath,
- NewFilePath: e.NewFilePath,
- OldContent: []byte(e.OldContent),
- NewContent: []byte(e.NewContent),
- IsDelete: e.IsDelete,
- IsCreate: e.IsCreate,
- ExtraFiles: extra,
- IDMap: e.IDMap,
- })
- }
- return changes
- }
- us.history = fromEntries(saveData.History)
- log.Printf("undo: loaded %d history entries", len(us.history))
-}
-
func (us *UndoStack) Clear() {
us.mu.Lock()
defer us.mu.Unlock()
diff --git a/internal/admin/xterm_to_css.go b/internal/admin/xterm_to_css.go
index 098ce62..6c1fda4 100644
--- a/internal/admin/xterm_to_css.go
+++ b/internal/admin/xterm_to_css.go
@@ -25,16 +25,3 @@ func XtermColorForCSS(idx int) string {
r, g, b := color.Xterm256ToRGB(idx)
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
}
-
-func xtermColorList() []map[string]any {
- var list []map[string]any
- for i := 0; i < 256; i++ {
- r, g, b := color.Xterm256ToRGB(i)
- list = append(list, map[string]any{
- "index": i,
- "hex": fmt.Sprintf("%02X%02X%02X", r, g, b),
- "color": fmt.Sprintf("#%02x%02x%02x", r, g, b),
- })
- }
- return list
-}
diff --git a/internal/admin/yaml_util.go b/internal/admin/yaml_util.go
index 9ef8058..6ea6692 100644
--- a/internal/admin/yaml_util.go
+++ b/internal/admin/yaml_util.go
@@ -142,23 +142,6 @@ func listYAMLTree(dataDir, subdir string) (*YAMLTree, error) {
return tree, nil
}
-func listYAMLFilesDeep(dataDir, subdir string) ([]string, error) {
- var ids []string
- base := filepath.Join(dataDir, subdir)
- err := filepath.WalkDir(base, func(path string, d os.DirEntry, err error) error {
- if err != nil {
- return err
- }
- if !d.IsDir() && filepath.Ext(d.Name()) == ".yaml" {
- rel, _ := filepath.Rel(base, path)
- id := rel[:len(rel)-5]
- ids = append(ids, id)
- }
- return nil
- })
- return ids, err
-}
-
func backupFile(path string) []byte {
data, err := os.ReadFile(path)
if err != nil {
diff --git a/internal/casino/slots.go b/internal/casino/slots.go
index c519293..075050c 100644
--- a/internal/casino/slots.go
+++ b/internal/casino/slots.go
@@ -71,15 +71,6 @@ func (p SlotProfile) Normalize() SlotProfile {
return p
}
-func (p SlotProfile) symbol(id string) *SlotSymbol {
- for i := range p.Symbols {
- if p.Symbols[i].ID == id {
- return &p.Symbols[i]
- }
- }
- return nil
-}
-
func generateSlotSpinWithProfile(rng *rand.Rand, bet int, profile SlotProfile) *SlotSpin {
p := profile.Normalize()
spin := &SlotSpin{}
diff --git a/internal/config/config.go b/internal/config/config.go
index 72c1c87..ffc4e50 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -103,12 +103,12 @@ func DefaultConstantColors() ColorsConfig {
"task_bar_low": "43",
// Score-panel other bars and tech/buff indicators (cmd_score.go).
- "battery_bar": "6E",
- "run_energy_bar": "6C",
- "active_tech_on": "6C",
- "active_tech_status": "6C",
- "active_tech_stat": "6E",
- "score_max_value": "FA",
+ "battery_bar": "6E",
+ "run_energy_bar": "6C",
+ "active_tech_on": "6C",
+ "active_tech_status": "6C",
+ "active_tech_stat": "6E",
+ "score_max_value": "FA",
// Skill-level tier color on the score panel (cmd_score.go).
"skill_lvl_max": "B4",
@@ -120,7 +120,7 @@ func DefaultConstantColors() ColorsConfig {
// Level-difference band on mobs/players (render_color.go).
"level_diff_equal": "FA",
"level_diff_up_small": "B3",
- "level_diff_up_big": "A7",
+ "level_diff_up_big": "A7",
"level_diff_down_small": "6E",
"level_diff_down_big": "41",
@@ -133,11 +133,11 @@ func DefaultConstantColors() ColorsConfig {
// Shared table-heading chrome (cmd_skills.go, cmd_mods.go,
// cmd_option.go, cmd_aps.go).
- "table_header_label": "49",
- "table_header_value": "FA",
+ "table_header_label": "49",
+ "table_header_value": "FA",
"table_header_detail": "F5",
- "table_header_xp": "B3",
- "table_header_desc": "FF",
+ "table_header_xp": "B3",
+ "table_header_desc": "FF",
// APS / look inline accents.
"aps_here": "6C",
diff --git a/internal/engine/tick.go b/internal/engine/tick.go
index 74b1dcf..63ee6af 100644
--- a/internal/engine/tick.go
+++ b/internal/engine/tick.go
@@ -99,8 +99,11 @@ func (e *Engine) Stop() {
// before owned state is saved.
func (e *Engine) StopAndWait() {
e.Stop()
+ // Join any in-flight tick: processTick holds tickMu for its whole body,
+ // so acquiring the mutex blocks until the running callback completes;
+ // holding it until return keeps that guarantee for the caller.
e.tickMu.Lock()
- e.tickMu.Unlock()
+ defer e.tickMu.Unlock()
}
// Tick fires every due subscriber synchronously and returns. Subscribers
diff --git a/internal/game/account_txn.go b/internal/game/account_txn.go
index 2ed62bb..25cbcdb 100644
--- a/internal/game/account_txn.go
+++ b/internal/game/account_txn.go
@@ -109,11 +109,6 @@ func (g *Game) backfillCharacterIDs() {
}
}
-// canonicAccount returns the account name in its canonical on-disk form.
-func (g *Game) canonicalAccount(name string) (string, error) {
- return g.AccountStore.FindAccount(name)
-}
-
// canonicChar returns the character name in its canonical on-disk form.
func (g *Game) canonicalChar(name string) (string, error) {
return g.AccountStore.FindCharacter(name)
diff --git a/internal/game/act_burn.go b/internal/game/act_burn.go
index c24d3d7..b310145 100644
--- a/internal/game/act_burn.go
+++ b/internal/game/act_burn.go
@@ -145,7 +145,7 @@ func (g *Game) startStoke(sess *net.Session, p *player.Player, itemID string) {
logDef, err := g.ItemStore.Load(itemID)
if err != nil || logDef.BurnTicks <= 0 {
- sess.WriteLine(fmt.Sprintf("You can't stoke the fire with that."))
+ sess.WriteLine("You can't stoke the fire with that.")
return
}
diff --git a/internal/game/act_farm.go b/internal/game/act_farm.go
index 8ed0d1b..cc7ddc4 100644
--- a/internal/game/act_farm.go
+++ b/internal/game/act_farm.go
@@ -456,55 +456,6 @@ func (g *Game) toolShedLookSuffix(p *player.Player) string {
return fmt.Sprintf("Inside: %s.", strings.Join(stored, ", "))
}
-func (g *Game) farmMatchPrefix(p *player.Player, input string) (prefix string, defID string, index int) {
- objInstances := g.World.AllObjInstances(p.RoomID)
- if len(objInstances) == 0 {
- return "", "", -1
- }
-
- type indexedFarm struct {
- prefix string
- defID string
- index int
- name string
- }
- var farms []indexedFarm
-
- for _, obj := range objInstances {
- if !farmPatchDefIDs[obj.DefID] {
- continue
- }
- pref := farmFlagPrefix(obj.DefID, obj.Index)
- if pref == "" {
- continue
- }
- var name string
- if def, err := g.ObjectStore.Load(obj.DefID); err == nil {
- name = def.Name
- } else {
- name = obj.DefID
- }
- farms = append(farms, indexedFarm{prefix: pref, defID: obj.DefID, index: obj.Index, name: name})
- }
-
- if len(farms) == 0 {
- return "", "", -1
- }
-
- if input == "" {
- return farms[0].prefix, farms[0].defID, farms[0].index
- }
-
- lower := strings.ToLower(input)
- for _, f := range farms {
- if strings.Contains(lower, strings.ToLower(f.name)) || behavior.WordPrefixMatch(lower, f.name) {
- return f.prefix, f.defID, f.index
- }
- }
-
- return "", "", -1
-}
-
func (g *Game) farmObjSuffix(p *player.Player, defID string, index int) string {
if defID == "tool_shed" {
return g.toolShedLookSuffix(p)
@@ -559,7 +510,7 @@ func (g *Game) showFarmPatches(sess *net.Session, p *player.Player) []string {
if weeds {
displays = append(displays, farmDisplay{
name: fmt.Sprintf("%s%s", patchName, idxStr),
- line: fmt.Sprintf("overgrown with weeds"),
+ line: "overgrown with weeds",
})
continue
}
@@ -568,7 +519,7 @@ func (g *Game) showFarmPatches(sess *net.Session, p *player.Player) []string {
if seedID == "" {
displays = append(displays, farmDisplay{
name: fmt.Sprintf("%s%s", patchName, idxStr),
- line: fmt.Sprintf("empty and ready for planting"),
+ line: "empty and ready for planting",
})
continue
}
@@ -582,7 +533,7 @@ func (g *Game) showFarmPatches(sess *net.Session, p *player.Player) []string {
if dead {
displays = append(displays, farmDisplay{
name: fmt.Sprintf("%s%s", patchName, idxStr),
- line: fmt.Sprintf("contains a dead plant"),
+ line: "contains a dead plant",
})
continue
}
diff --git a/internal/game/act_gather.go b/internal/game/act_gather.go
index 0701fc4..447e23a 100644
--- a/internal/game/act_gather.go
+++ b/internal/game/act_gather.go
@@ -178,7 +178,7 @@ func (g *Game) advanceGather(sess *net.Session, p *player.Player) {
return
}
}
- sess.WriteLine(fmt.Sprintf("%s", cfg.GatherMessage))
+ sess.WriteLine(cfg.GatherMessage)
d.Step = 1
p.Action.WaitLeft = engine.ToTicks(wait)
return
diff --git a/internal/game/act_room.go b/internal/game/act_room.go
index 889ce85..792d8bd 100644
--- a/internal/game/act_room.go
+++ b/internal/game/act_room.go
@@ -75,11 +75,3 @@ func (g *Game) BroadcastRespawns() {
}
}
}
-
-// isCharLive reports whether the named player is currently connected via the
-// given session (used by older helpers that still reference it).
-func (g *Game) isCharLive(name string, sess *net.Session) bool {
- g.charsMu.Lock()
- defer g.charsMu.Unlock()
- return g.loggedInChars[name] == sess
-}
diff --git a/internal/game/baseline_race_test.go b/internal/game/baseline_race_test.go
index de5b7b4..fc07c5e 100644
--- a/internal/game/baseline_race_test.go
+++ b/internal/game/baseline_race_test.go
@@ -6,7 +6,6 @@ package game
// pass under -race with the single-owner design in place.
import (
- "fmt"
"sync"
"testing"
"thehouseoficarus/internal/net"
@@ -72,7 +71,7 @@ func TestBaselineRaceAdminRewriteWhilePlaying(t *testing.T) {
go func() {
defer wg.Done()
for i := 0; i < 200; i++ {
- lines <- fmt.Sprintf("go north")
+ lines <- "go north"
}
close(lines)
}()
diff --git a/internal/game/cmd_drop.go b/internal/game/cmd_drop.go
index d77eb53..674f3da 100644
--- a/internal/game/cmd_drop.go
+++ b/internal/game/cmd_drop.go
@@ -229,7 +229,7 @@ func (g *Game) handleDropAll(sess *net.Session, input string) {
if len(dropped) == 0 {
sess.WriteLine("You have nothing to drop.")
} else {
- sess.Write(fmt.Sprintf("You drop your "))
+ sess.Write("You drop your ")
formatPickupList(sess, dropped)
}
g.writePrompt(sess)
diff --git a/internal/game/cmd_estate_directory.go b/internal/game/cmd_estate_directory.go
index b28e634..2441157 100644
--- a/internal/game/cmd_estate_directory.go
+++ b/internal/game/cmd_estate_directory.go
@@ -49,7 +49,7 @@ func (g *Game) lookEstateDirectory(sess *net.Session) {
homeowners := g.scanHomeowners("owns_house_local_neighborhood")
sess.WriteLine("")
sess.WriteLine(g.colorize(sess, "dialog", "Estate Directory"))
- sess.WriteLine(fmt.Sprintf(" A holographic terminal displaying property records for the Local Neighborhood."))
+ sess.WriteLine(" A holographic terminal displaying property records for the Local Neighborhood.")
sess.WriteLine("")
if len(homeowners) == 0 {
sess.WriteLine(" No registered homeowners in this area.")
diff --git a/internal/game/cmd_farm.go b/internal/game/cmd_farm.go
index 19cdf12..aaa3779 100644
--- a/internal/game/cmd_farm.go
+++ b/internal/game/cmd_farm.go
@@ -373,76 +373,6 @@ func (g *Game) doCure(sess *net.Session, input string) {
g.AccountStore.SaveCharacter(p)
}
-func (g *Game) doInspect(sess *net.Session, input string) {
- p := sess.Player
-
- objInstances := g.World.AllObjInstances(p.RoomID)
- var farmPatches []struct {
- prefix string
- defID string
- index int
- }
-
- for _, obj := range objInstances {
- if !farmPatchDefIDs[obj.DefID] {
- continue
- }
- pref := farmFlagPrefix(obj.DefID, obj.Index)
- if pref == "" {
- continue
- }
- farmPatches = append(farmPatches, struct {
- prefix string
- defID string
- index int
- }{pref, obj.DefID, obj.Index})
- }
-
- if len(farmPatches) == 0 {
- sess.WriteLine("There are no farming patches here.")
- return
- }
-
- if input != "" {
- lower := strings.ToLower(input)
- var filtered []struct {
- prefix string
- defID string
- index int
- }
- for _, fp := range farmPatches {
- def, _ := g.ObjectStore.Load(fp.defID)
- if def != nil && strings.Contains(strings.ToLower(def.Name), lower) {
- filtered = append(filtered, fp)
- }
- }
- if len(filtered) == 0 {
- for _, fp := range farmPatches {
- if strings.Contains(fp.prefix, lower) || strings.Contains(strings.ToLower(fp.defID), lower) {
- filtered = append(filtered, fp)
- }
- }
- }
- farmPatches = filtered
- }
-
- for _, fp := range farmPatches {
- g.ensureFarmState(p, fp.prefix)
- def, _ := g.ObjectStore.Load(fp.defID)
- patchName := fp.defID
- if def != nil {
- patchName = def.Name
- }
- indexStr := ""
- if len(farmPatches) > 1 || fp.index > 0 {
- indexStr = fmt.Sprintf(" %d", fp.index+1)
- }
-
- sess.WriteLine(fmt.Sprintf("=== %s%s ===", patchName, indexStr))
- g.ShowFarmPatchDetail(sess, p, fp.prefix, fp.defID, fp.index)
- }
-}
-
func (g *Game) ShowFarmPatchDetail(sess *net.Session, p *player.Player, prefix, defID string, index int) {
weeds, _ := p.Flags[prefix+"_weeds"].(bool)
if weeds {
@@ -465,7 +395,7 @@ func (g *Game) ShowFarmPatchDetail(sess *net.Session, p *player.Player, prefix,
dead, _ := p.Flags[prefix+"_dead"].(bool)
if dead {
- sess.WriteLine(fmt.Sprintf(" Status: Dead"))
+ sess.WriteLine(" Status: Dead")
sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName))
sess.WriteLine(" (Rake to clear)")
return
@@ -473,7 +403,7 @@ func (g *Game) ShowFarmPatchDetail(sess *net.Session, p *player.Player, prefix,
diseased, _ := p.Flags[prefix+"_diseased"].(bool)
if diseased {
- sess.WriteLine(fmt.Sprintf(" Status: Diseased!"))
+ sess.WriteLine(" Status: Diseased!")
sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName))
sess.WriteLine(" (Use plant cure to save it)")
return
@@ -481,7 +411,7 @@ func (g *Game) ShowFarmPatchDetail(sess *net.Session, p *player.Player, prefix,
ready, _ := p.Flags[prefix+"_ready"].(bool)
if ready {
- sess.WriteLine(fmt.Sprintf(" Status: Ready to harvest!"))
+ sess.WriteLine(" Status: Ready to harvest!")
sess.WriteLine(fmt.Sprintf(" Planted: %s", seedName))
return
}
diff --git a/internal/game/cmd_queued.go b/internal/game/cmd_queued.go
index 549c5fb..7ac05a6 100644
--- a/internal/game/cmd_queued.go
+++ b/internal/game/cmd_queued.go
@@ -43,13 +43,13 @@ func (g *Game) doQueued(sess *net.Session) {
if activeCmd.Args != "" {
cmd += " " + activeCmd.Args
}
- sess.WriteLine(fmt.Sprintf(" Queued active action (executes after free actions):"))
- sess.WriteLine(fmt.Sprintf(" %s", cmd))
+ sess.WriteLine(" Queued active action (executes after free actions):")
+ sess.WriteLine(" " + cmd)
}
sess.WriteLine("")
- sess.WriteLine(fmt.Sprintf(" All queued actions take effect on the next game tick (600ms)."))
- sess.WriteLine(fmt.Sprintf(" Free actions stack. Only the most recent active action survives."))
+ sess.WriteLine(" All queued actions take effect on the next game tick (600ms).")
+ sess.WriteLine(" Free actions stack. Only the most recent active action survives.")
names, _ := g.queue.ActivePositions(p.Name)
sort.Strings(names)
diff --git a/internal/game/cmd_remove.go b/internal/game/cmd_remove.go
index 3a451af..38f17a3 100644
--- a/internal/game/cmd_remove.go
+++ b/internal/game/cmd_remove.go
@@ -38,7 +38,7 @@ func (g *Game) doRemove(sess *net.Session, input string) {
if !ok {
continue
}
- if strings.ToLower(string(slot)) == strings.ToLower(itemName) {
+ if strings.EqualFold(string(slot), itemName) {
foundSlot = slot
foundItemID = itemID
break
diff --git a/internal/game/cmd_room_remove_test.go b/internal/game/cmd_room_remove_test.go
index cd9569d..97ab859 100644
--- a/internal/game/cmd_room_remove_test.go
+++ b/internal/game/cmd_room_remove_test.go
@@ -71,14 +71,6 @@ func assertOutContains(t *testing.T, sess *net.Session, sub string) {
}
}
-func assertOutNotContains(t *testing.T, sess *net.Session, sub string) {
- t.Helper()
- out := sess.Conn.(*removeTestConn).output()
- if strings.Contains(out, sub) {
- t.Errorf("expected session output to NOT contain %q, got:\n%s", sub, out)
- }
-}
-
// reloadAfterCmd re-reads a room from disk after a command has mutated it.
func reloadRoom(t *testing.T, g *Game, id int) *world.Room {
t.Helper()
diff --git a/internal/game/cmd_tech.go b/internal/game/cmd_tech.go
index 0a5580c..1431228 100644
--- a/internal/game/cmd_tech.go
+++ b/internal/game/cmd_tech.go
@@ -126,7 +126,7 @@ func (g *Game) doTechList(sess *net.Session) {
for _, tech := range AllTechs {
status := ""
- nameStr := tech.Name
+ var nameStr string
if tech.Level <= techLevel {
if p.HasActiveTech(tech.ID) {
status = color.Render(mode, g.resolveConstant(sess, "tech_status_on"), "ON")
diff --git a/internal/game/cmd_undig.go b/internal/game/cmd_undig.go
index a41cb08..c24b299 100644
--- a/internal/game/cmd_undig.go
+++ b/internal/game/cmd_undig.go
@@ -110,7 +110,7 @@ func (g *Game) listUndigOrphans(sess *net.Session, targetID int) {
if len(orphans) > 0 {
sess.WriteLine(g.colorize(sess, "warning",
- fmt.Sprintf("WARNING: The following rooms will become unreachable after deletion:")))
+ "WARNING: The following rooms will become unreachable after deletion:"))
for _, rid := range orphans {
room, err := g.World.LoadRoom(rid)
name := fmt.Sprintf("#%d", rid)
@@ -154,7 +154,7 @@ func (g *Game) handleUndigConfirm(sess *net.Session, input string) {
func (g *Game) performUndig(sess *net.Session, p *player.Player, targetID int) {
targetRoom, err := g.World.LoadRoom(targetID)
- roomName := fmt.Sprintf("#%d", targetID)
+ var roomName string
if err == nil {
roomName = fmt.Sprintf("#%d (%s)", targetID, targetRoom.Name)
} else {
@@ -234,7 +234,7 @@ func (g *Game) performUndig(sess *net.Session, p *player.Player, targetID int) {
if g.Hub != nil {
g.Hub.EnterRoom(other, p.RoomID)
}
- other.WriteLine(fmt.Sprintf("The room around you dissolves. You find yourself elsewhere."))
+ other.WriteLine("The room around you dissolves. You find yourself elsewhere.")
g.doLook(other)
}
}
diff --git a/internal/game/cmd_use.go b/internal/game/cmd_use.go
index 975eddb..12dbc73 100644
--- a/internal/game/cmd_use.go
+++ b/internal/game/cmd_use.go
@@ -192,7 +192,7 @@ func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName,
itemAID := matchesA[0].ID
- matchesB := g.findInventoryMatches(itemBName, p)
+ var matchesB []itemMatch
objInstances := g.World.FindObjInstances(p.RoomID, itemBName)
if len(objInstances) > 0 {
diff --git a/internal/game/cmd_walk.go b/internal/game/cmd_walk.go
index 80600af..101c0fe 100644
--- a/internal/game/cmd_walk.go
+++ b/internal/game/cmd_walk.go
@@ -53,7 +53,7 @@ func parseWalkDirections(input string) ([]string, error) {
num = 1
}
if i >= len(input) {
- return nil, fmt.Errorf("Unexpected end of directions.")
+ return nil, fmt.Errorf("unexpected end of directions")
}
ch := input[i]
i++
@@ -88,7 +88,7 @@ func parseWalkDirections(input string) ([]string, error) {
case 'd':
dir = string(world.Down)
default:
- return nil, fmt.Errorf("Unknown direction: %c", ch)
+ return nil, fmt.Errorf("unknown direction: %c", ch)
}
for j := 0; j < num; j++ {
dirs = append(dirs, dir)
diff --git a/internal/game/core_course.go b/internal/game/core_course.go
index 15bd47e..03b091e 100644
--- a/internal/game/core_course.go
+++ b/internal/game/core_course.go
@@ -143,11 +143,7 @@ func (cs *CourseStore) GetObstacle(roomID int) *ObstacleInfo {
func resolvePhases(obs ObstacleDef) []PhaseInfo {
phases := make([]PhaseInfo, 0, len(obs.Phases))
for _, p := range obs.Phases {
- phases = append(phases, PhaseInfo{
- Message: p.Message,
- Delay: p.Delay,
- FailCheck: p.FailCheck,
- })
+ phases = append(phases, PhaseInfo(p))
}
return phases
}
diff --git a/internal/game/core_login_account.go b/internal/game/core_login_account.go
index 1acd1a6..4c05373 100644
--- a/internal/game/core_login_account.go
+++ b/internal/game/core_login_account.go
@@ -379,7 +379,7 @@ func (g *Game) handleRenameAccount(sess *net.Session, input string) {
func (g *Game) handlePurgeAccount(sess *net.Session, input string) {
input = strings.TrimSpace(input)
expected := "PURGE " + sess.Account.Name
- if strings.ToUpper(input) != strings.ToUpper(expected) {
+ if !strings.EqualFold(input, expected) {
sess.WriteLine("Purge cancelled.")
g.showMenu(sess)
return
diff --git a/internal/game/core_login_char.go b/internal/game/core_login_char.go
index 8388bfb..c21ea87 100644
--- a/internal/game/core_login_char.go
+++ b/internal/game/core_login_char.go
@@ -261,7 +261,7 @@ func (g *Game) handleDeleteChar(sess *net.Session, input string) {
input = strings.TrimSpace(input)
expected := "DELETE " + sess.PendingChar
- if strings.ToUpper(input) != strings.ToUpper(expected) {
+ if !strings.EqualFold(input, expected) {
sess.WriteLine("Delete cancelled.")
sess.PendingChar = ""
g.showMenu(sess)
diff --git a/internal/game/harness_test.go b/internal/game/harness_test.go
index 7898af2..a368634 100644
--- a/internal/game/harness_test.go
+++ b/internal/game/harness_test.go
@@ -75,14 +75,6 @@ func (c *chanConn) Contains(substr string) bool {
return strings.Contains(string(c.Bytes()), substr)
}
-// send queues an input line for ReadMessage. No-op after close.
-func (c *chanConn) send(line string) {
- select {
- case c.in <- line:
- case <-c.closed:
- }
-}
-
// harness wires a real Game (temp data dir) to fake transports driven from
// per-connection goroutines plus a competing tick goroutine, reproducing the
// production entry-point concurrency: session input, disconnect callbacks, and
@@ -255,12 +247,3 @@ func (h *harness) goInput(sess *net.Session, workers int) chan<- string {
}
return lines
}
-
-const workers = 1
-
-// sessionFor returns the registered session for a character name.
-func (h *harness) sessionFor(name string) *net.Session {
- h.mu.Lock()
- defer h.mu.Unlock()
- return h.sessions[name]
-}
diff --git a/internal/game/owner.go b/internal/game/owner.go
index ce1f687..9c7be64 100644
--- a/internal/game/owner.go
+++ b/internal/game/owner.go
@@ -177,18 +177,6 @@ func (g *Game) RunAdminOnOwner(fn func()) bool {
return accepted
}
-// tryRun executes fn on the owner if it is currently idle-draining; it is
-// not currently used for correctness paths but exists for future non-blocking
-// administrative reads. It returns false when the queue is full.
-func (g *Game) tryRun(fn func()) bool {
- o := g.owner
- if o == nil {
- fn()
- return true
- }
- return o.submit(fn)
-}
-
// currentGoroutineID returns the calling goroutine's ID parsed from its
// stack header ("goroutine 123 [running]:"), 0 when unparseable.
func currentGoroutineID() uint64 {
diff --git a/internal/game/production_index.go b/internal/game/production_index.go
index 77fc08a..672dcae 100644
--- a/internal/game/production_index.go
+++ b/internal/game/production_index.go
@@ -153,15 +153,6 @@ func (idx *CraftIndex) FindByStationInput(stationDefID, itemID string) []*item.I
return results
}
-func craftEntryFor(def *item.ItemDef, itemID string) int {
- for _, craft := range def.Craft {
- if ei := craftEntry(craft, itemID); ei >= 0 {
- return ei
- }
- }
- return -1
-}
-
func craftEntry(craft item.CraftDef, itemID string) int {
for ei, e := range craft.Ingredients {
for _, id := range e.Items {
diff --git a/internal/game/render_help.go b/internal/game/render_help.go
index c7d0aa0..7886f48 100644
--- a/internal/game/render_help.go
+++ b/internal/game/render_help.go
@@ -224,13 +224,6 @@ func matchesHelpTopic(h HelpDef, topic string) bool {
return false
}
-func firstLine(s string) string {
- if idx := strings.Index(s, "\n"); idx >= 0 {
- return s[:idx]
- }
- return s
-}
-
func (g *Game) executeHelp(sess *net.Session, args []string, rawInput string) {
if len(args) == 0 {
g.doHelp(sess, "")
diff --git a/internal/game/sys_assassin.go b/internal/game/sys_assassin.go
index 65ddb7c..fc814ec 100644
--- a/internal/game/sys_assassin.go
+++ b/internal/game/sys_assassin.go
@@ -2,68 +2,12 @@ package game
import (
"fmt"
- "math/rand"
"thehouseoficarus/internal/net"
"thehouseoficarus/internal/player"
"thehouseoficarus/internal/world"
)
-type assassinTaskEntry struct {
- MobID string
- MinLevel int
- MaxLevel int
- MinCount int
- MaxCount int
- Weight int
-}
-
-var assassinTaskTable = []assassinTaskEntry{
- {"man", 1, 15, 10, 25, 8},
- {"cow", 1, 15, 10, 25, 8},
- {"slug", 1, 99, 15, 45, 15},
- {"drone", 15, 99, 20, 50, 12},
- {"crawler", 30, 99, 15, 40, 10},
- {"phantom", 45, 99, 10, 30, 8},
-}
-
-func (g *Game) assignAssassinTask(sess *net.Session, p *player.Player) {
- level := p.Level(player.Assassin)
- var eligible []assassinTaskEntry
- totalWeight := 0
- for _, entry := range assassinTaskTable {
- if level >= entry.MinLevel && level <= entry.MaxLevel {
- eligible = append(eligible, entry)
- totalWeight += entry.Weight
- }
- }
- if len(eligible) == 0 {
- sess.WriteLine("The Client shakes their head. \"Nothing available for your level.\"")
- return
- }
- roll := rand.Intn(totalWeight)
- var chosen assassinTaskEntry
- for _, entry := range eligible {
- roll -= entry.Weight
- if roll < 0 {
- chosen = entry
- break
- }
- }
- count := chosen.MinCount + rand.Intn(chosen.MaxCount-chosen.MinCount+1)
- g.setPlayerFlag(p, "assassin_task_mob", chosen.MobID)
- g.setPlayerFlag(p, "assassin_task_total", count)
- g.setPlayerFlag(p, "assassin_task_remaining", count)
- g.AccountStore.SaveCharacter(p)
-
- def, err := g.MobStore.LoadDef(chosen.MobID)
- name := chosen.MobID
- if err == nil {
- name = def.Name
- }
- sess.WriteLine(g.colorize(sess, "assassin_task", fmt.Sprintf("\"Your target: %d %ss. Get to work.\"", count, name)))
-}
-
func (g *Game) onAssassinKill(sess *net.Session, p *player.Player, mob *world.MobInstance) {
taskMob := getPlayerFlagString(p, "assassin_task_mob")
if taskMob == "" || taskMob != mob.DefID {
@@ -93,7 +37,7 @@ func (g *Game) onAssassinKill(sess *net.Session, p *player.Player, mob *world.Mo
rep += bonus
currentRep := getPlayerFlagInt(p, "assassin_reputation")
g.setPlayerFlag(p, "assassin_reputation", currentRep+rep)
- sess.WriteLine(g.colorize(sess, "assassin_task", fmt.Sprintf("*** Assassin task complete! ***")))
+ sess.WriteLine(g.colorize(sess, "assassin_task", "*** Assassin task complete! ***"))
if bonus > 0 {
sess.WriteLine(g.colorize(sess, "assassin_task", fmt.Sprintf(" Streak bonus! %d tasks in a row. +%d bonus reputation.", streak, bonus)))
}
@@ -110,50 +54,6 @@ func (g *Game) onAssassinKill(sess *net.Session, p *player.Player, mob *world.Mo
g.AccountStore.SaveCharacter(p)
}
-func (g *Game) skipAssassinTask(sess *net.Session, p *player.Player) {
- rep := getPlayerFlagInt(p, "assassin_reputation")
- if rep < 30 {
- sess.WriteLine("You don't have enough Reputation to skip. (Need 30, have " + fmt.Sprint(rep) + ")")
- return
- }
- g.setPlayerFlag(p, "assassin_reputation", rep-30)
- delete(p.Flags, "assassin_task_mob")
- g.setPlayerFlag(p, "assassin_task_remaining", 0)
- g.setPlayerFlag(p, "assassin_task_total", 0)
- g.setPlayerFlag(p, "assassin_streak", 0)
- g.AccountStore.SaveCharacter(p)
-}
-
-func (g *Game) extendAssassinTask(sess *net.Session, p *player.Player) {
- rep := getPlayerFlagInt(p, "assassin_reputation")
- if rep < 30 {
- sess.WriteLine("You don't have enough Reputation to extend. (Need 30, have " + fmt.Sprint(rep) + ")")
- return
- }
- taskMob := getPlayerFlagString(p, "assassin_task_mob")
- if taskMob == "" {
- sess.WriteLine("You don't have an active task to extend.")
- return
- }
- g.setPlayerFlag(p, "assassin_reputation", rep-30)
- total := getPlayerFlagInt(p, "assassin_task_total")
- remaining := getPlayerFlagInt(p, "assassin_task_remaining")
- extension := total / 2
- if extension < 5 {
- extension = 5
- }
- g.setPlayerFlag(p, "assassin_task_total", total+extension)
- g.setPlayerFlag(p, "assassin_task_remaining", remaining+extension)
- g.AccountStore.SaveCharacter(p)
-
- def, _ := g.MobStore.LoadDef(taskMob)
- name := taskMob
- if def != nil {
- name = def.Name
- }
- sess.WriteLine(fmt.Sprintf("Task extended by %d. Kill %d more %ss (%d total).", extension, remaining+extension, name, total+extension))
-}
-
func streakBonus(streak int) int {
if streak%1000 == 0 {
return 50
diff --git a/internal/game/sys_buff.go b/internal/game/sys_buff.go
index 3b8555c..01660d8 100644
--- a/internal/game/sys_buff.go
+++ b/internal/game/sys_buff.go
@@ -3,7 +3,6 @@ package game
import (
"strings"
- "thehouseoficarus/internal/net"
"thehouseoficarus/internal/player"
)
@@ -49,7 +48,3 @@ func (g *Game) buffLevelBonus(p *player.Player, stat string) int {
baseLevel := p.Level(player.SkillName(strings.ToLower(stat)))
return baseLevel * totalPercent / 100
}
-
-func (g *Game) totalBuffedLevel(sess *net.Session, p *player.Player, stat string) int {
- return p.Level(player.SkillName(strings.ToLower(stat))) + g.buffLevelBonus(p, stat)
-}
diff --git a/internal/player/validate.go b/internal/player/validate.go
index 1a2a5b9..015b3d8 100644
--- a/internal/player/validate.go
+++ b/internal/player/validate.go
@@ -17,7 +17,7 @@ func ValidName(name string) error {
func ValidPassword(pw string) error {
if len(pw) > 128 {
- return fmt.Errorf("Password too long.")
+ return fmt.Errorf("password too long")
}
return nil
}
diff --git a/internal/validate/checks.go b/internal/validate/checks.go
index 344df3a..6994654 100644
--- a/internal/validate/checks.go
+++ b/internal/validate/checks.go
@@ -782,51 +782,51 @@ func validateDropTables(s Source) []Issue {
if d.ItemID != "" && d.Table != "" {
issues = append(issues, Issue{
- Level: "WARN",
- Type: "integrity",
+ Level: "WARN",
+ Type: "integrity",
Message: fmt.Sprintf("Drop table %q entry %d: both item_id and table set (only one is used per entry)", id, i+1),
})
}
if d.ItemID == "" && d.Table == "" {
issues = append(issues, Issue{
- Level: "WARN",
- Type: "integrity",
+ Level: "WARN",
+ Type: "integrity",
Message: fmt.Sprintf("Drop table %q entry %d: neither item_id nor table set (roll produces nothing)", id, i+1),
})
}
if d.Depletes {
issues = append(issues, Issue{
- Level: "WARN",
- Type: "integrity",
+ Level: "WARN",
+ Type: "integrity",
Message: fmt.Sprintf("Drop table %q entry %d: 'depletes' is ignored on shared drop table entries; set it on the gather entry that references this table instead", id, i+1),
})
}
if d.Level > 0 {
issues = append(issues, Issue{
- Level: "WARN",
- Type: "integrity",
+ Level: "WARN",
+ Type: "integrity",
Message: fmt.Sprintf("Drop table %q entry %d: 'level' is ignored on shared drop table entries; set it on the gather entry that references this table", id, i+1),
})
}
if d.XP > 0 {
issues = append(issues, Issue{
- Level: "WARN",
- Type: "integrity",
+ Level: "WARN",
+ Type: "integrity",
Message: fmt.Sprintf("Drop table %q entry %d: 'xp' is ignored on shared drop table entries; set it on the gather entry that references this table", id, i+1),
})
}
if d.Tool != "" {
issues = append(issues, Issue{
- Level: "WARN",
- Type: "integrity",
+ Level: "WARN",
+ Type: "integrity",
Message: fmt.Sprintf("Drop table %q entry %d: 'tool' is ignored on shared drop table entries; set it on the gather entry that references this table", id, i+1),
})
}
if d.SuccessMessage != "" {
issues = append(issues, Issue{
- Level: "WARN",
- Type: "integrity",
+ Level: "WARN",
+ Type: "integrity",
Message: fmt.Sprintf("Drop table %q entry %d: 'success_message' is ignored on shared drop table entries; set it on the gather entry that references this table", id, i+1),
})
}