Adopt house golangci-lint config; fix all approved-linter findings

.golangci.yml is the prompts-repo standard verbatim plus one approved
exception (paralleltest, sneak 2026-07-06). Roughly 1,500 findings
fixed:

- autofix sweep (wsl_v5/nlreturn/intrange/modernize formatting), with
  the misspell autofix REVERTED where it rewrote authentic C game text
  ("missle vanishes" stays, with nolint and a comment)
- error handling: errcheck sites get real handling — saveFile now
  closes explicitly and removes corrupt saves, scoreboard writes are
  documented best-effort, err113 sentinel errors (ErrSaveOutOfDate,
  ErrScreenTooSmall); panics allowed for unrecoverable states per
  MEMORY.md policy
- gosec: real fixes (ParseInt for SEED, 0600 scorefile) and justified
  per-line nolints for provably-bounded conversions (randomMonsterLetter
  helper collapses eight rnd(26)+'A' sites)
- API tidying from linters: pointer receivers on flag types
  (recvcheck), msg helpers renamed addmsgf/doaddf/Printwf/MvPrintwf
  (goprintffuncname), myExit()/findFloor(monst)/mkGameInput(t) drop
  always-constant params (unparam), gameEnd carries no status
- gocritic/staticcheck: if-else chains to switches, the pack.c
  inventory filter untangled into matchesFilter, main.go split so
  defers run before exit (exitAfterDefer, funlen)
- revive doc comments on all exported flag types/consts/methods

Remaining findings are confined to linters pending sneak's exception
decision (mnd, gochecknoglobals, cyclop, nestif, gocognit, exhaustive,
goconst, testpackage); TODO.md records the state. MEMORY.md added at
repo root as the project's agent working notes.
This commit is contained in:
2026-07-07 00:03:45 +02:00
parent 3ed7931676
commit 5ba9fe8f66
49 changed files with 1965 additions and 410 deletions

View File

@@ -6,6 +6,7 @@ package game
// bracketing (command.c command).
func (g *RogueGame) command() {
p := &g.Player
ntimes := 1 // number of player moves
if p.On(Hasted) {
ntimes++
@@ -13,6 +14,7 @@ func (g *RogueGame) command() {
// Let the daemons start up
g.DoDaemons(Before)
g.DoFuses(Before)
for ; ntimes > 0; ntimes-- {
g.Again = false
if g.HasHit {
@@ -26,29 +28,37 @@ func (g *RogueGame) command() {
}
g.look(true)
if !g.Running {
g.DoorStop = false
}
g.status()
g.LastScore = p.Purse
g.move(p.Pos.Y, p.Pos.X)
if !((g.Running || g.Count != 0) && g.Options.Jump) {
if (!g.Running && g.Count == 0) || !g.Options.Jump {
g.refresh() // draw screen
}
g.Take = 0
g.After = true
// Read command or continue run
if g.Wizard {
g.NoScore = true
}
var ch byte
if g.NoCommand == 0 {
if g.Running || g.ToDeath {
switch {
case g.Running || g.ToDeath:
ch = g.RunCh
} else if g.Count != 0 {
case g.Count != 0:
ch = g.countCh
} else {
default:
ch = g.readchar()
g.MoveOn = false
if g.Msgs.Mpos != 0 { // erase message if its there
g.msg("")
@@ -57,6 +67,7 @@ func (g *RogueGame) command() {
} else {
ch = '.'
}
if g.NoCommand != 0 {
if g.NoCommand--; g.NoCommand == 0 {
p.Flags.Set(Awake)
@@ -67,14 +78,14 @@ func (g *RogueGame) command() {
g.newCount = false
if isDigit(ch) {
g.Count = 0
g.newCount = true
for isDigit(ch) {
g.Count = g.Count*10 + int(ch-'0')
if g.Count > 255 {
g.Count = 255
}
g.Count = min(g.Count*10+int(ch-'0'), 255)
ch = g.readchar()
}
g.countCh = ch
// turn off count for commands which don't make sense
// to repeat
@@ -93,8 +104,9 @@ func (g *RogueGame) command() {
if g.Count != 0 && !g.Running {
g.Count--
}
if ch != 'a' && ch != Escape &&
!(g.Running || g.Count != 0 || g.ToDeath) {
!g.Running && g.Count == 0 && !g.ToDeath {
g.LLastComm = g.LastComm
g.LLastDir = g.LastDir
g.LLastPick = g.LastPick
@@ -102,6 +114,7 @@ func (g *RogueGame) command() {
g.LastDir = 0
g.LastPick = nil
}
g.dispatch(ch)
// turn off flags if no longer needed
if !g.Running {
@@ -112,20 +125,25 @@ func (g *RogueGame) command() {
if g.Take != 0 {
g.pickUp(g.Take)
}
if !g.Running {
g.DoorStop = false
}
if !g.After {
ntimes++
}
}
g.DoDaemons(After)
g.DoFuses(After)
if p.IsRing(Left, RingSearching) {
g.search()
} else if p.IsRing(Left, RingTeleportation) && g.rnd(50) == 0 {
g.teleport()
}
if p.IsRing(Right, RingSearching) {
g.search()
} else if p.IsRing(Right, RingTeleportation) && g.rnd(50) == 0 {
@@ -137,28 +155,35 @@ func (g *RogueGame) command() {
// including its `goto over` re-dispatch).
func (g *RogueGame) dispatch(ch byte) {
p := &g.Player
over:
switch ch {
case ',':
var found *Object
for _, obj := range g.Level.Objects {
if obj.Pos.Y == p.Pos.Y && obj.Pos.X == p.Pos.X {
found = obj
break
}
}
if found != nil {
if !g.levitCheck() {
g.pickUp(found.Kind.Glyph())
}
} else {
if !g.Options.Terse {
g.addmsg("there is ")
g.addmsgf("there is ")
}
g.addmsg("nothing here")
g.addmsgf("nothing here")
if !g.Options.Terse {
g.addmsg(" to pick up")
g.addmsgf(" to pick up")
}
g.endmsg()
}
case '!':
@@ -201,36 +226,46 @@ over:
g.DoorStop = true
g.Firstmove = true
}
if g.Count != 0 && !g.newCount {
ch = g.direction
} else {
ch += 'A' - CTRL('A')
g.direction = ch
}
goto over
case 'F', 'f':
if ch == 'F' {
g.Kamikaze = true
}
if !g.getDir() {
g.After = false
break
}
g.Delta.Y += p.Pos.Y
g.Delta.X += p.Pos.X
mp := g.Level.MonsterAt(g.Delta.Y, g.Delta.X)
if mp == nil || (!g.seeMonst(mp) && !p.On(SenseMonsters)) {
if !g.Options.Terse {
g.addmsg("I see ")
g.addmsgf("I see ")
}
g.msg("no monster there")
g.After = false
} else if g.diagOk(p.Pos, g.Delta) {
g.ToDeath = true
g.MaxHit = 0
mp.Flags.Set(Targeted)
g.RunCh = g.DirCh
ch = g.DirCh
goto over
}
case 't':
@@ -246,6 +281,7 @@ over:
} else {
ch = g.LastComm
g.Again = true
goto over
}
case 'q':
@@ -327,15 +363,18 @@ over:
if g.getDir() {
g.Delta.Y += p.Pos.Y
g.Delta.X += p.Pos.X
fp := g.Level.FlagsAt(g.Delta.Y, g.Delta.X)
if !g.Options.Terse {
g.addmsg("You have found ")
g.addmsgf("You have found ")
}
if g.Level.Char(g.Delta.Y, g.Delta.X) != Trap {
switch {
case g.Level.Char(g.Delta.Y, g.Delta.X) != Trap:
g.msg("no trap there")
} else if p.On(Hallucinating) {
case p.On(Hallucinating):
g.msg("%s", trName[g.rnd(NumTrapTypes)])
} else {
default:
g.msg("%s", trName[*fp&FTrapMask])
fp.Set(FSeen)
}
@@ -352,6 +391,7 @@ over:
} else {
ch = g.DirCh
g.countCh = g.DirCh
goto over
}
case ')':
@@ -381,6 +421,7 @@ over:
// wizardCommand handles the MASTER debug commands (command.c).
func (g *RogueGame) wizardCommand(ch byte) {
p := &g.Player
switch ch {
case '|':
g.msg("@ %d,%d", p.Pos.Y, p.Pos.X)
@@ -452,62 +493,81 @@ func (g *RogueGame) search() {
p := &g.Player
ey := p.Pos.Y + 1
ex := p.Pos.X + 1
probinc := 0
if p.On(Hallucinating) {
probinc = 3
}
if p.On(Blind) {
probinc += 2
}
found := false
for y := p.Pos.Y - 1; y <= ey; y++ {
for x := p.Pos.X - 1; x <= ex; x++ {
if y == p.Pos.Y && x == p.Pos.X {
continue
}
fp := g.Level.FlagsAt(y, x)
if fp.Has(FReal) {
continue
}
foundone := false
switch g.Level.Char(y, x) {
case '|', '-':
if g.rnd(5+probinc) != 0 {
break
}
g.Level.SetChar(y, x, Door)
g.msg("a secret door")
foundone = true
case Floor:
if g.rnd(2+probinc) != 0 {
break
}
g.Level.SetChar(y, x, Trap)
if !g.Options.Terse {
g.addmsg("you found ")
g.addmsgf("you found ")
}
if p.On(Hallucinating) {
g.msg("%s", trName[g.rnd(NumTrapTypes)])
} else {
g.msg("%s", trName[*fp&FTrapMask])
fp.Set(FSeen)
}
foundone = true
case ' ':
if g.rnd(3+probinc) != 0 {
break
}
g.Level.SetChar(y, x, Passage)
foundone = true
}
if foundone {
found = true
fp.Set(FReal)
g.Count = 0
g.Running = false
}
}
}
if found {
g.look(false)
}
@@ -523,28 +583,35 @@ func (g *RogueGame) help() {
// typed a funny character.
if helpch != '*' {
g.move(0, 0)
for _, strp := range helpStr {
if strp.Ch == helpch {
g.Msgs.LowerMsg = true
g.msg("%s%s", unctrl(strp.Ch), strp.Desc)
g.Msgs.LowerMsg = false
return
}
}
g.msg("unknown character '%s'", unctrl(helpch))
return
}
// Here we print help for everything, then wait before we return to
// command mode.
numprint := 0
for _, strp := range helpStr {
if strp.Print {
numprint++
}
}
if numprint&1 == 1 { // round odd numbers up
numprint++
}
numprint /= 2
if numprint > NumLines-1 {
numprint = NumLines - 1
@@ -552,24 +619,32 @@ func (g *RogueGame) help() {
hw := g.scr.Hw
hw.Clear()
cnt := 0
for _, strp := range helpStr {
if !strp.Print {
continue
}
x := 0
if cnt >= numprint {
x = NumCols / 2
}
hw.Move(cnt%numprint, x)
if strp.Ch != 0 {
hw.AddStr(unctrl(strp.Ch))
}
hw.AddStr(strp.Desc)
if cnt++; cnt >= numprint*2 {
break
}
}
hw.MvAddStr(NumLines-1, 0, "--Press space to continue--")
g.scr.RefreshWin(hw)
g.waitFor(' ')
@@ -603,23 +678,29 @@ var identList = []helpEntry{
func (g *RogueGame) identify() {
g.msg("what do you want identified? ")
ch := g.readchar()
g.Msgs.Mpos = 0
if ch == Escape {
g.msg("")
return
}
var str string
if isUpper(ch) {
str = g.Monsters[ch-'A'].Name
} else {
str = "unknown character"
for _, hp := range identList {
if hp.Ch == ch {
str = hp.Desc
break
}
}
}
g.msg("'%s': %s", unctrl(ch), str)
}
@@ -628,6 +709,7 @@ func (g *RogueGame) dLevel() {
if g.levitCheck() {
return
}
if g.Level.Char(g.Player.Pos.Y, g.Player.Pos.X) != Stairs {
g.msg("I see no way down")
} else {
@@ -642,12 +724,14 @@ func (g *RogueGame) uLevel() {
if g.levitCheck() {
return
}
if g.Level.Char(g.Player.Pos.Y, g.Player.Pos.X) == Stairs {
if g.HasAmulet {
g.Depth--
if g.Depth == 0 {
g.totalWinner()
}
g.NewLevel()
g.msg("you feel a wrenching sensation in your gut")
} else {
@@ -664,7 +748,9 @@ func (g *RogueGame) levitCheck() bool {
if !g.Player.On(Levitating) {
return false
}
g.msg("You can't. You're floating off the ground!")
return true
}
@@ -676,11 +762,16 @@ func (g *RogueGame) call() {
if obj == nil {
return
}
var op *ObjInfo
var elsewise string
var know *bool
var guess *string
var (
op *ObjInfo
elsewise string
know *bool
guess *string
)
it := &g.Items
switch obj.Kind {
case KindRing:
op = &it.Rings[obj.Which]
@@ -696,14 +787,18 @@ func (g *RogueGame) call() {
elsewise = it.WandMade[obj.Which]
case KindFood:
g.msg("you can't call that anything")
return
default:
guess = &obj.Label
elsewise = obj.Label
}
fromGuess := false
if op != nil {
know = &op.Know
guess = &op.Guess
if *guess != "" {
elsewise = *guess
@@ -712,16 +807,21 @@ func (g *RogueGame) call() {
} else {
fromGuess = elsewise != "" && elsewise == *guess
}
if know != nil && *know {
g.msg("that has already been identified")
return
}
if fromGuess {
if !g.Options.Terse {
g.addmsg("Was ")
g.addmsgf("Was ")
}
g.msg("called \"%s\"", elsewise)
}
g.msg("%s", g.chooseTerse("call it: ", "what do you want to call it? "))
buf := elsewise
@@ -735,23 +835,29 @@ func (g *RogueGame) current(cur *Object, how, where string) {
g.After = false
if cur != nil {
if !g.Options.Terse {
g.addmsg("you are %s (", how)
g.addmsgf("you are %s (", how)
}
g.InvDescribe = false
g.addmsg("%c) %s", cur.PackCh, g.invName(cur, true))
g.addmsgf("%c) %s", cur.PackCh, g.invName(cur, true))
g.InvDescribe = true
if where != "" {
g.addmsg(" %s", where)
g.addmsgf(" %s", where)
}
g.endmsg()
} else {
if !g.Options.Terse {
g.addmsg("you are ")
g.addmsgf("you are ")
}
g.addmsg("%s nothing", how)
g.addmsgf("%s nothing", how)
if where != "" {
g.addmsg(" %s", where)
g.addmsgf(" %s", where)
}
g.endmsg()
}
}
@@ -762,7 +868,9 @@ func (g *RogueGame) shell() {
g.After = false
if se, ok := g.scr.term.(interface{ ShellEscape() }); ok {
g.InShell = true
se.ShellEscape()
g.InShell = false
g.refresh()
} else {