diff options
| author | historia <[not public]> | 2026-09-16 15:00:46 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-09-16 15:00:46 -0400 |
| commit | a6cf03a6ae98e4fb675da25046d148e92bcf7206 (patch) | |
| tree | 194499fe9e696be8c31cd915b7ad92678f9f95e0 /internal | |
| parent | 9192d94dea6be5498ab080f3e2df5667af871d7a (diff) | |
| download | thehouseoficarus-a6cf03a6ae98e4fb675da25046d148e92bcf7206.tar.gz | |
chore: staticcheck and gofmt cleanups
Diffstat (limited to 'internal')
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), }) } |
