diff --git a/TODO.md b/TODO.md index 13079ab..7269bea 100644 --- a/TODO.md +++ b/TODO.md @@ -29,14 +29,26 @@ Refactor ground rules: # Next Step -Refactor step 4: method renames, movement/world subsystem -(doMove→moveHero, beTrapped→springTrap, rndmove→randomStep, -doRooms/doPassages/doMaze→digRooms/digPassages/digMaze, -chgStr→changeStrength, ...); remove the goto/label flows in doMove, -dispatch, and saveGame in favor of loops and helpers. +Refactor step 5: method renames, items/combat/UI subsystems +(invName→inventoryName, rollEm→rollAttacks, doPot→applyPotionFuse, +getItem→promptPackItem returning (obj, ok), getDir→promptDirection); +int status codes (attack returning -1) become named results. Two or +three commits, one subsystem each. # Completed Steps +- 2026-07-07 Refactor step 4 (refactor/movement-renames): movement/world + renames (doMove→moveHero, beTrapped→springTrap, rndmove→randomStep, + doRooms/doPassages/doMaze→digRooms/digPassages/digMaze, + chgStr→changeStrength, doRun→startRun, moveStuff→finishMove, + turnref→turnRefresh, moveMonst→moveMonster, doChase→chaseStep, + setOldch→setOldChar, cansee→canSee, roomin→roomIn, runto→runTo, + conn→connectRooms, putpass→putPassage, passnum→numberPassages, + numpass→numberPassage, rndPos→randomPos, rndRoom→randomRoom, + treasRoom→treasureRoom, accntMaze→accountMaze); all goto/label flows + replaced with loops (moveHero retry loop + extracted passageTurn, + dispatch re-dispatch loop, chaseStep passage loop, saveGame labeled + prompt loop); C breadcrumbs kept in doc comments. - 2026-07-07 Lint adoption finished (refactor/no-package-globals): all 37 package-level vars moved into `gameData` (built by `newGameData`, hung on RogueGame as `g.data`, set in NewGame and Restore); ObjectKind @@ -98,41 +110,36 @@ dispatch, and saveGame in favor of loops and helpers. # Future Steps -1. Refactor step 5: method renames, items/combat/UI subsystems - (invName→inventoryName, rollEm→rollAttacks, doPot→applyPotionFuse, - getItem→promptPackItem returning (obj, ok), getDir→promptDirection); - int status codes (attack returning -1) become named results. Two or - three commits, one subsystem each. -2. Refactor step 6: extract types from the god object — MessageLine +1. Refactor step 6: extract types from the god object — MessageLine owns the msg/addmsg/endmsg machinery; pack/inventory operations move onto *Player; monster/object list management and map queries consolidate onto *Level; RogueGame keeps turn orchestration and cross-system effects only. -3. Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap +2. Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap switches become per-kind handler tables of small named methods, keeping effect order and RNG call sequence identical. This step also clears the outstanding cyclop/gocognit/nestif lint findings. -4. Refactor step 8: constructor and style pass per the house +3. Refactor step 8: constructor and style pass per the house styleguide — game.New(game.Params{...}) replacing NewGame(Config); replace the gameEnd panic unwind with error-based turn results where feasible; 77-column wrap sweep. -5. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the +4. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the post-refactor names; add the C name → Go name rename table. -6. Playtest hardening pass: play several full games with the tcell +5. Playtest hardening pass: play several full games with the tcell binary and extend run_test.go to script a deeper multi-level playthrough (descend past level 5, use potions, scrolls, zapping, save/restore). Fix any panics, message mismatches, or divergences from the C behavior that this uncovers, with regression tests. -7. Verify the seed-compatibility claim against the C reference on +6. Verify the seed-compatibility claim against the C reference on c-master: same seed, same dungeon, same item tables, for several seeds. -8. Broaden unit test coverage where playtesting finds thin spots +7. Broaden unit test coverage where playtesting finds thin spots (rings, sticks, wizard commands). -9. Tag a release once a full game (Amulet retrieval and score entry) +8. Tag a release once a full game (Amulet retrieval and score entry) completes without defects. -10. Full-terminal-size support (deferred by explicit decision - 2026-07-06): per-game dungeon dimensions instead of the 80x24 - constants; open design questions are resize policy, gameplay - tuning at larger sizes, and a --classic 80x24 mode. -11. Note: this repo is exempt from the standard policy scaffold. Do not +9. Full-terminal-size support (deferred by explicit decision + 2026-07-06): per-game dungeon dimensions instead of the 80x24 + constants; open design questions are resize policy, gameplay + tuning at larger sizes, and a --classic 80x24 mode. +10. Note: this repo is exempt from the standard policy scaffold. Do not add Makefile, Dockerfile, or REPO_POLICIES.md. diff --git a/game/chase.go b/game/chase.go index 2086cef..5e6f5cf 100644 --- a/game/chase.go +++ b/game/chase.go @@ -13,12 +13,12 @@ func (g *RogueGame) runners(int) { origPos := tp.Pos wastarget := tp.On(Targeted) - if g.moveMonst(tp) == -1 { + if g.moveMonster(tp) == -1 { continue } if tp.On(Flying) && distCp(g.Player.Pos, tp.Pos) >= 3 { - if g.moveMonst(tp) == -1 { + if g.moveMonster(tp) == -1 { continue } } @@ -37,17 +37,17 @@ func (g *RogueGame) runners(int) { } } -// moveMonst executes a single turn of running for a monster (chase.c +// moveMonster executes a single turn of running for a monster (chase.c // move_monst). Returns -1 if the monster died or left the level. -func (g *RogueGame) moveMonst(tp *Monster) int { +func (g *RogueGame) moveMonster(tp *Monster) int { if !tp.On(Slowed) || tp.Turn { - if g.doChase(tp) == -1 { + if g.chaseStep(tp) == -1 { return -1 } } if tp.On(Hasted) { - if g.doChase(tp) == -1 { + if g.chaseStep(tp) == -1 { return -1 } } @@ -62,8 +62,8 @@ func (g *RogueGame) moveMonst(tp *Monster) int { func (g *RogueGame) relocate(th *Monster, newLoc Coord) { if newLoc != th.Pos { g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh) - th.Room = g.roomin(newLoc) - g.setOldch(th, newLoc) + th.Room = g.roomIn(newLoc) + g.setOldChar(th, newLoc) oroom := th.Room g.Level.SetMonsterAt(th.Pos.Y, th.Pos.X, nil) @@ -86,9 +86,9 @@ func (g *RogueGame) relocate(th *Monster, newLoc Coord) { } } -// doChase makes one thing chase another (chase.c do_chase). Returns -1 if -// the chaser died in the attempt. -func (g *RogueGame) doChase(th *Monster) int { +// chaseStep makes one thing chase another (chase.c do_chase). Returns -1 +// if the chaser died in the attempt. +func (g *RogueGame) chaseStep(th *Monster) int { p := &g.Player stoprun := false // true means we are there mindist := 32767 @@ -102,59 +102,62 @@ func (g *RogueGame) doChase(th *Monster) int { if th.Dest == &p.Pos { ree = p.Room } else { - ree = g.roomin(*th.Dest) + ree = g.roomIn(*th.Dest) } // We don't count doors as inside rooms for this routine door := g.Level.Char(th.Pos.Y, th.Pos.X) == Door var this Coord -over: - // If the object of our desire is in a different room, and we are not - // in a corridor, run to the door nearest to our goal. - if rer != ree { - for i := range rer.Exits { - curdist := distCp(*th.Dest, rer.Exits[i]) - if curdist < mindist { - this = rer.Exits[i] - mindist = curdist + for { + // If the object of our desire is in a different room, and we are + // not in a corridor, run to the door nearest to our goal. + if rer != ree { + for i := range rer.Exits { + curdist := distCp(*th.Dest, rer.Exits[i]) + if curdist < mindist { + this = rer.Exits[i] + mindist = curdist + } + } + + if door { + rer = &g.Level.Passages[*g.Level.FlagsAt(th.Pos.Y, th.Pos.X)&FPassNum] + door = false + + continue // the C goto over: redo with the passage as room + } + } else { + this = *th.Dest + // For dragons check and see if (a) the hero is on a straight + // line from it, and (b) that it is within shooting distance, + // but outside of striking range. + if th.Type == 'D' && (th.Pos.Y == p.Pos.Y || th.Pos.X == p.Pos.X || + abs(th.Pos.Y-p.Pos.Y) == abs(th.Pos.X-p.Pos.X)) && + distCp(th.Pos, p.Pos) <= BoltLength*BoltLength && + !th.On(Cancelled) && g.rnd(dragonShot) == 0 { + g.Delta.Y = sign(p.Pos.Y - th.Pos.Y) + + g.Delta.X = sign(p.Pos.X - th.Pos.X) + if g.HasHit { + g.endmsg() + } + + g.fireBolt(th.Pos, &g.Delta, "flame") + g.Running = false + g.Count = 0 + + g.Quiet = 0 + if g.ToDeath && !th.On(Targeted) { + g.ToDeath = false + g.Kamikaze = false + } + + return 0 } } - if door { - rer = &g.Level.Passages[*g.Level.FlagsAt(th.Pos.Y, th.Pos.X)&FPassNum] - door = false - - goto over - } - } else { - this = *th.Dest - // For dragons check and see if (a) the hero is on a straight line - // from it, and (b) that it is within shooting distance, but - // outside of striking range. - if th.Type == 'D' && (th.Pos.Y == p.Pos.Y || th.Pos.X == p.Pos.X || - abs(th.Pos.Y-p.Pos.Y) == abs(th.Pos.X-p.Pos.X)) && - distCp(th.Pos, p.Pos) <= BoltLength*BoltLength && - !th.On(Cancelled) && g.rnd(dragonShot) == 0 { - g.Delta.Y = sign(p.Pos.Y - th.Pos.Y) - - g.Delta.X = sign(p.Pos.X - th.Pos.X) - if g.HasHit { - g.endmsg() - } - - g.fireBolt(th.Pos, &g.Delta, "flame") - g.Running = false - g.Count = 0 - - g.Quiet = 0 - if g.ToDeath && !th.On(Targeted) { - g.ToDeath = false - g.Kamikaze = false - } - - return 0 - } + break } // This now contains what we want to run to this time so we run to it. // If we hit it we either want to fight it or stop running @@ -214,7 +217,7 @@ func (g *RogueGame) chase(tp *Monster, ee Coord) bool { if (tp.On(Confused) && g.rnd(5) != 0) || (tp.Type == 'P' && g.rnd(5) == 0) || (tp.Type == 'B' && g.rnd(2) == 0) { // get a valid random move - g.chRet = g.rndmove(&tp.Creature) + g.chRet = g.randomStep(&tp.Creature) curdist = distCp(g.chRet, ee) // Small chance that it will become un-confused if g.rnd(20) == 0 { @@ -294,8 +297,8 @@ func (g *RogueGame) chase(tp *Monster, ee Coord) bool { return curdist != 0 && g.chRet != p.Pos } -// setOldch sets the oldch character for the monster (chase.c set_oldch). -func (g *RogueGame) setOldch(tp *Monster, cp Coord) { +// setOldChar sets the oldch character for the monster (chase.c set_oldch). +func (g *RogueGame) setOldChar(tp *Monster, cp Coord) { if tp.Pos == cp { return } @@ -341,8 +344,8 @@ func (g *RogueGame) seeMonst(mp *Monster) bool { return !mp.Room.Flags.Has(Dark) } -// runto sets a monster running after the hero (chase.c runto). -func (g *RogueGame) runto(runner Coord) { +// runTo sets a monster running after the hero (chase.c runto). +func (g *RogueGame) runTo(runner Coord) { tp := g.Level.MonsterAt(runner.Y, runner.X) if tp == nil { return @@ -353,9 +356,9 @@ func (g *RogueGame) runto(runner Coord) { tp.Dest = g.findDest(tp) } -// roomin finds what room some coordinates are in; nil means they aren't in +// roomIn finds what room some coordinates are in; nil means they aren't in // any room (chase.c roomin). -func (g *RogueGame) roomin(cp Coord) *Room { +func (g *RogueGame) roomIn(cp Coord) *Room { fp := *g.Level.FlagsAt(cp.Y, cp.X) if fp.Has(FPassage) { return &g.Level.Passages[fp&FPassNum] @@ -387,9 +390,9 @@ func (g *RogueGame) diagOk(sp, ep Coord) bool { return stepOk(g.Level.Char(ep.Y, sp.X)) && stepOk(g.Level.Char(sp.Y, ep.X)) } -// cansee returns true if the hero can see a certain coordinate (chase.c +// canSee returns true if the hero can see a certain coordinate (chase.c // cansee). -func (g *RogueGame) cansee(y, x int) bool { +func (g *RogueGame) canSee(y, x int) bool { p := &g.Player if p.On(Blind) { return false @@ -408,7 +411,7 @@ func (g *RogueGame) cansee(y, x int) bool { } // We can only see if the hero is in the same room as the coordinate // and the room is lit, or if it is close. - rer := g.roomin(Coord{X: x, Y: y}) + rer := g.roomIn(Coord{X: x, Y: y}) return rer == p.Room && !rer.Flags.Has(Dark) } @@ -426,7 +429,7 @@ func (g *RogueGame) findDest(tp *Monster) *Coord { continue } - if g.roomin(obj.Pos) == tp.Room && g.rnd(100) < prob { + if g.roomIn(obj.Pos) == tp.Room && g.rnd(100) < prob { claimed := false for _, other := range g.Level.Monsters { diff --git a/game/command.go b/game/command.go index 3e5d704..70e1349 100644 --- a/game/command.go +++ b/game/command.go @@ -151,270 +151,273 @@ func (g *RogueGame) command() { } } -// dispatch runs one command character (the big switch in command.c, -// including its `goto over` re-dispatch). +// dispatch runs one command character (the big switch in command.c; the +// loop stands in for its `goto over` re-dispatch). func (g *RogueGame) dispatch(ch byte) { p := &g.Player -over: - switch ch { - case ',': - var found *Object + for { + 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 + 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.addmsgf("there is ") + } + + g.addmsgf("nothing here") + + if !g.Options.Terse { + g.addmsgf(" to pick up") + } + + g.endmsg() + } + case '!': + g.shell() + case 'h': + g.moveHero(0, -1) + case 'j': + g.moveHero(1, 0) + case 'k': + g.moveHero(-1, 0) + case 'l': + g.moveHero(0, 1) + case 'y': + g.moveHero(-1, -1) + case 'u': + g.moveHero(-1, 1) + case 'b': + g.moveHero(1, -1) + case 'n': + g.moveHero(1, 1) + case 'H': + g.startRun('h') + case 'J': + g.startRun('j') + case 'K': + g.startRun('k') + case 'L': + g.startRun('l') + case 'Y': + g.startRun('y') + case 'U': + g.startRun('u') + case 'B': + g.startRun('b') + case 'N': + g.startRun('n') + case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'), + CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'): + if !p.On(Blind) { + g.DoorStop = true + g.Firstmove = true + } + + if g.Count != 0 && !g.newCount { + ch = g.direction + } else { + ch += 'A' - CTRL('A') + g.direction = ch + } + + continue // the C goto over: re-dispatch as the run command + case 'F', 'f': + if ch == 'F' { + g.Kamikaze = true + } + + if !g.getDir() { + g.After = false break } - } - if found != nil { - if !g.levitCheck() { - g.pickUp(found.Kind.Glyph()) - } - } else { - if !g.Options.Terse { - g.addmsgf("there is ") - } - - g.addmsgf("nothing here") - - if !g.Options.Terse { - g.addmsgf(" to pick up") - } - - g.endmsg() - } - case '!': - g.shell() - case 'h': - g.doMove(0, -1) - case 'j': - g.doMove(1, 0) - case 'k': - g.doMove(-1, 0) - case 'l': - g.doMove(0, 1) - case 'y': - g.doMove(-1, -1) - case 'u': - g.doMove(-1, 1) - case 'b': - g.doMove(1, -1) - case 'n': - g.doMove(1, 1) - case 'H': - g.doRun('h') - case 'J': - g.doRun('j') - case 'K': - g.doRun('k') - case 'L': - g.doRun('l') - case 'Y': - g.doRun('y') - case 'U': - g.doRun('u') - case 'B': - g.doRun('b') - case 'N': - g.doRun('n') - case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'), - CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'): - if !p.On(Blind) { - 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.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': - if !g.getDir() { - g.After = false - } else { - g.missile(g.Delta.Y, g.Delta.X) - } - case 'a': - if g.LastComm == 0 { - g.msg("you haven't typed a command yet") - g.After = false - } else { - ch = g.LastComm - g.Again = true - - goto over - } - case 'q': - g.quaff() - case 'Q': - g.After = false - g.QComm = true - g.quit(0) - g.QComm = false - case 'i': - g.After = false - g.inventory(p.Pack, 0) - case 'I': - g.After = false - g.pickyInven() - case 'd': - g.dropIt() - case 'r': - g.readScroll() - case 'e': - g.eat() - case 'w': - g.wield() - case 'W': - g.wear() - case 'T': - g.takeOff() - case 'P': - g.ringOn() - case 'R': - g.ringOff() - case 'o': - g.option() - g.After = false - case 'c': - g.call() - g.After = false - case '>': - g.After = false - g.dLevel() - case '<': - g.After = false - g.uLevel() - case '?': - g.After = false - g.help() - case '/': - g.After = false - g.identify() - case 's': - g.search() - case 'z': - if g.getDir() { - g.doZap() - } else { - g.After = false - } - case 'D': - g.After = false - g.discovered() - case CTRL('P'): - g.After = false - g.msg("%s", g.Msgs.Huh) - case CTRL('R'): - g.After = false - g.refresh() - case 'v': - g.After = false - g.msg("version %s. (mctesq was here)", Release) - case 'S': - g.After = false - g.saveGame() - case '.': - // rest command - case ' ': - g.After = false // "legal" illegal command - case '^': - g.After = false - 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.addmsgf("You have found ") - } + mp := g.Level.MonsterAt(g.Delta.Y, g.Delta.X) + if mp == nil || (!g.seeMonst(mp) && !p.On(SenseMonsters)) { + if !g.Options.Terse { + g.addmsgf("I see ") + } - switch { - case g.Level.Char(g.Delta.Y, g.Delta.X) != Trap: - g.msg("no trap there") - case p.On(Hallucinating): - g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)]) - default: - g.msg("%s", g.data.trName[*fp&FTrapMask]) - fp.Set(FSeen) + 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 + + continue // the C goto over: fight by running at it } - } - case Escape: // escape - g.DoorStop = false - g.Count = 0 - g.After = false - g.Again = false - case 'm': - g.MoveOn = true - if !g.getDir() { + case 't': + if !g.getDir() { + g.After = false + } else { + g.missile(g.Delta.Y, g.Delta.X) + } + case 'a': + if g.LastComm == 0 { + g.msg("you haven't typed a command yet") + g.After = false + } else { + ch = g.LastComm + g.Again = true + + continue // the C goto over: replay the last command + } + case 'q': + g.quaff() + case 'Q': g.After = false - } else { - ch = g.DirCh - g.countCh = g.DirCh + g.QComm = true + g.quit(0) + g.QComm = false + case 'i': + g.After = false + g.inventory(p.Pack, 0) + case 'I': + g.After = false + g.pickyInven() + case 'd': + g.dropIt() + case 'r': + g.readScroll() + case 'e': + g.eat() + case 'w': + g.wield() + case 'W': + g.wear() + case 'T': + g.takeOff() + case 'P': + g.ringOn() + case 'R': + g.ringOff() + case 'o': + g.option() + g.After = false + case 'c': + g.call() + g.After = false + case '>': + g.After = false + g.dLevel() + case '<': + g.After = false + g.uLevel() + case '?': + g.After = false + g.help() + case '/': + g.After = false + g.identify() + case 's': + g.search() + case 'z': + if g.getDir() { + g.doZap() + } else { + g.After = false + } + case 'D': + g.After = false + g.discovered() + case CTRL('P'): + g.After = false + g.msg("%s", g.Msgs.Huh) + case CTRL('R'): + g.After = false + g.refresh() + case 'v': + g.After = false + g.msg("version %s. (mctesq was here)", Release) + case 'S': + g.After = false + g.saveGame() + case '.': + // rest command + case ' ': + g.After = false // "legal" illegal command + case '^': + g.After = false + if g.getDir() { + g.Delta.Y += p.Pos.Y + g.Delta.X += p.Pos.X - goto over - } - case ')': - g.current(p.CurWeapon, "wielding", "") - case ']': - g.current(p.CurArmor, "wearing", "") - case '=': - g.current(p.CurRing[Left], "wearing", - g.chooseTerse("(L)", "on left hand")) - g.current(p.CurRing[Right], "wearing", - g.chooseTerse("(R)", "on right hand")) - case '@': - g.StatMsg = true - g.status() - g.StatMsg = false - g.After = false - default: - g.After = false - if g.Wizard { - g.wizardCommand(ch) - } else { - g.illcom(ch) + fp := g.Level.FlagsAt(g.Delta.Y, g.Delta.X) + if !g.Options.Terse { + g.addmsgf("You have found ") + } + + switch { + case g.Level.Char(g.Delta.Y, g.Delta.X) != Trap: + g.msg("no trap there") + case p.On(Hallucinating): + g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)]) + default: + g.msg("%s", g.data.trName[*fp&FTrapMask]) + fp.Set(FSeen) + } + } + case Escape: // escape + g.DoorStop = false + g.Count = 0 + g.After = false + g.Again = false + case 'm': + g.MoveOn = true + if !g.getDir() { + g.After = false + } else { + ch = g.DirCh + g.countCh = g.DirCh + + continue // the C goto over: move onto it without pickup + } + case ')': + g.current(p.CurWeapon, "wielding", "") + case ']': + g.current(p.CurArmor, "wearing", "") + case '=': + g.current(p.CurRing[Left], "wearing", + g.chooseTerse("(L)", "on left hand")) + g.current(p.CurRing[Right], "wearing", + g.chooseTerse("(R)", "on right hand")) + case '@': + g.StatMsg = true + g.status() + g.StatMsg = false + g.After = false + default: + g.After = false + if g.Wizard { + g.wizardCommand(ch) + } else { + g.illcom(ch) + } } + + return } } diff --git a/game/daemons.go b/game/daemons.go index 684dba1..e78307c 100644 --- a/game/daemons.go +++ b/game/daemons.go @@ -212,7 +212,7 @@ func (g *RogueGame) comeDown(int) { // undo the things for _, tp := range g.Level.Objects { - if g.cansee(tp.Pos.Y, tp.Pos.X) { + if g.canSee(tp.Pos.Y, tp.Pos.X) { g.mvaddch(tp.Pos.Y, tp.Pos.X, tp.Kind.Glyph()) } } @@ -223,7 +223,7 @@ func (g *RogueGame) comeDown(int) { for _, tp := range g.Level.Monsters { g.move(tp.Pos.Y, tp.Pos.X) - if g.cansee(tp.Pos.Y, tp.Pos.X) { + if g.canSee(tp.Pos.Y, tp.Pos.X) { if !tp.On(Invisible) || p.On(CanSeeInvisible) { g.addch(tp.Disguise) } else { @@ -248,13 +248,13 @@ func (g *RogueGame) visuals(int) { } // change the things for _, tp := range g.Level.Objects { - if g.cansee(tp.Pos.Y, tp.Pos.X) { + if g.canSee(tp.Pos.Y, tp.Pos.X) { g.mvaddch(tp.Pos.Y, tp.Pos.X, g.rndThing()) } } // change the stairs - if !g.SeenStairs && g.cansee(g.Level.Stairs.Y, g.Level.Stairs.X) { + if !g.SeenStairs && g.canSee(g.Level.Stairs.Y, g.Level.Stairs.X) { g.mvaddch(g.Level.Stairs.Y, g.Level.Stairs.X, g.rndThing()) } diff --git a/game/effects_test.go b/game/effects_test.go index 852b48e..b432e68 100644 --- a/game/effects_test.go +++ b/game/effects_test.go @@ -10,7 +10,7 @@ func mkGameInput(t *testing.T) *RogueGame { g := NewGame(Config{Seed: 5, Term: &testTerm{}}) g.NewLevel() g.Oldpos = g.Player.Pos - g.Oldrp = g.roomin(g.Player.Pos) + g.Oldrp = g.roomIn(g.Player.Pos) return g } diff --git a/game/fight.go b/game/fight.go index 6511cc2..b607a05 100644 --- a/game/fight.go +++ b/game/fight.go @@ -47,7 +47,7 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool { // place. g.Count = 0 g.Quiet = 0 - g.runto(mp) + g.runTo(mp) // Let him know it was really a xeroc (if it was one). if tp.Type == 'X' && tp.Disguise != 'X' && !p.On(Blind) { tp.Disguise = 'X' @@ -182,7 +182,7 @@ func (g *RogueGame) attack(mp *Monster) int { // Rattlesnakes have poisonous bites if !g.save(VsPoison) { if !p.IsWearing(RingSustainStrength) { - g.chgStr(-1) + g.changeStrength(-1) if !g.Options.Terse { g.msg("you feel a bite in your leg and now feel weaker") diff --git a/game/fight_test.go b/game/fight_test.go index 0b2d6df..bd72056 100644 --- a/game/fight_test.go +++ b/game/fight_test.go @@ -10,7 +10,7 @@ func mkGame(t *testing.T, seed int32) *RogueGame { g := NewGame(Config{Seed: seed, Term: &testTerm{}}) g.NewLevel() g.Oldpos = g.Player.Pos - g.Oldrp = g.roomin(g.Player.Pos) + g.Oldrp = g.roomIn(g.Player.Pos) return g } diff --git a/game/game.go b/game/game.go index 6d1cbbf..ecaa426 100644 --- a/game/game.go +++ b/game/game.go @@ -242,7 +242,7 @@ func (g *RogueGame) playit() { g.Oldpos = g.Player.Pos - g.Oldrp = g.roomin(g.Player.Pos) + g.Oldrp = g.roomIn(g.Player.Pos) for g.Playing { g.command() // command execution } diff --git a/game/misc.go b/game/misc.go index 60f8498..2390efe 100644 --- a/game/misc.go +++ b/game/misc.go @@ -305,9 +305,9 @@ func (g *RogueGame) checkLevel() { } } -// chgStr modifies the player's strength, keeping track of the highest it -// has been (misc.c chg_str). -func (g *RogueGame) chgStr(amt int) { +// changeStrength modifies the player's strength, keeping track of the +// highest it has been (misc.c chg_str). +func (g *RogueGame) changeStrength(amt int) { if amt == 0 { return } @@ -362,10 +362,10 @@ func (g *RogueGame) addHaste(potion bool) bool { // aggravate aggravates all the monsters on this level (misc.c aggravate). func (g *RogueGame) aggravate() { - // runto() can splice the monster list while we walk it, so iterate a copy. + // runTo() can splice the monster list while we walk it, so iterate a copy. monsters := append([]*Monster(nil), g.Level.Monsters...) for _, mp := range monsters { - g.runto(mp.Pos) + g.runTo(mp.Pos) } } diff --git a/game/monsters.go b/game/monsters.go index 864681f..7cf2611 100644 --- a/game/monsters.go +++ b/game/monsters.go @@ -37,7 +37,7 @@ func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) { tp.Pos = cp g.move(cp.Y, cp.X) tp.OldCh = g.inch() - tp.Room = g.roomin(cp) + tp.Room = g.roomIn(cp) g.Level.SetMonsterAt(cp.Y, cp.X, tp) mp := &g.Monsters[tp.Type-'A'] tp.Stats.Lvl = mp.Stats.Lvl + levAdd @@ -57,7 +57,7 @@ func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) { tp.Pack = nil if g.Player.IsWearing(RingAggravateMonsters) { - g.runto(cp) + g.runTo(cp) } if typ == 'X' { @@ -92,7 +92,7 @@ func (g *RogueGame) wanderer() { var cp Coord for { cp, _ = g.findFloor(true) - if g.roomin(cp) != g.Player.Room { + if g.roomIn(cp) != g.Player.Room { break } } @@ -111,7 +111,7 @@ func (g *RogueGame) wanderer() { g.standend() } - g.runto(tp.Pos) + g.runTo(tp.Pos) } // wakeMonster is what to do when the hero steps next to a monster diff --git a/game/move.go b/game/move.go index 5576597..f59ace6 100644 --- a/game/move.go +++ b/game/move.go @@ -2,16 +2,17 @@ package game // move.c — hero movement commands. -// doRun starts the hero running (move.c do_run). -func (g *RogueGame) doRun(ch byte) { +// startRun starts the hero running (move.c do_run). +func (g *RogueGame) startRun(ch byte) { g.Running = true g.After = false g.RunCh = ch } -// doMove checks that a move is legal and handles the consequences — -// fighting, picking up, etc. (move.c do_move). -func (g *RogueGame) doMove(dy, dx int) { +// moveHero checks that a move is legal and handles the consequences — +// fighting, picking up, etc. (move.c do_move). The C `goto over` +// re-check after a passage turn is the retry loop. +func (g *RogueGame) moveHero(dy, dx int) { p := &g.Player g.Firstmove = false @@ -24,7 +25,7 @@ func (g *RogueGame) doMove(dy, dx int) { // Do a confused move (maybe) var nh Coord if p.On(Confused) && g.rnd(5) != 0 { - nh = g.rndmove(&p.Creature) + nh = g.randomStep(&p.Creature) if nh == p.Pos { g.After = false g.Running = false @@ -36,150 +37,161 @@ func (g *RogueGame) doMove(dy, dx int) { nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx} } -over: - // Check if he tried to move off the screen or make an illegal diagonal - // move, and stop him if he did. - hitBound := nh.X < 0 || nh.X >= NumCols || nh.Y <= 0 || nh.Y >= NumLines-1 + for { + // Check if he tried to move off the screen or make an illegal + // diagonal move, and stop him if he did. + hitBound := nh.X < 0 || nh.X >= NumCols || nh.Y <= 0 || nh.Y >= NumLines-1 - var ( - ch byte - fl PlaceFlags - ) + var ( + ch byte + fl PlaceFlags + ) - if !hitBound { - if !g.diagOk(p.Pos, nh) { - g.After = false - g.Running = false + if !hitBound { + if !g.diagOk(p.Pos, nh) { + g.After = false + g.Running = false - return - } - - if g.Running && p.Pos == nh { - g.After = false - g.Running = false - } - - fl = *g.Level.FlagsAt(nh.Y, nh.X) - - ch = g.Level.VisibleChar(nh.Y, nh.X) - if !fl.Has(FReal) && ch == Floor { - if !p.On(Levitating) { - ch = Trap - g.Level.SetChar(nh.Y, nh.X, Trap) - g.Level.FlagsAt(nh.Y, nh.X).Set(FReal) + return } - } else if p.On(Held) && ch != 'F' { - g.msg("you are being held") - return - } - } + if g.Running && p.Pos == nh { + g.After = false + g.Running = false + } - if hitBound { - ch = ' ' // fall into the wall case below - } + fl = *g.Level.FlagsAt(nh.Y, nh.X) - switch ch { - case ' ', '|', '-': - if g.Options.PassGo && g.Running && p.Room.Flags.Has(Gone) && - !p.On(Blind) { - var b1, b2 bool - - switch g.RunCh { - case 'h', 'l': - b1 = p.Pos.Y != 1 && g.turnOk(p.Pos.Y-1, p.Pos.X) - - b2 = p.Pos.Y != NumLines-2 && g.turnOk(p.Pos.Y+1, p.Pos.X) - if b1 != b2 { - if b1 { - g.RunCh = 'k' - dy = -1 - } else { - g.RunCh = 'j' - dy = 1 - } - - dx = 0 - - g.turnref() - - nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx} - - goto over + ch = g.Level.VisibleChar(nh.Y, nh.X) + if !fl.Has(FReal) && ch == Floor { + if !p.On(Levitating) { + ch = Trap + g.Level.SetChar(nh.Y, nh.X, Trap) + g.Level.FlagsAt(nh.Y, nh.X).Set(FReal) } - case 'j', 'k': - b1 = p.Pos.X != 0 && g.turnOk(p.Pos.Y, p.Pos.X-1) + } else if p.On(Held) && ch != 'F' { + g.msg("you are being held") - b2 = p.Pos.X != NumCols-1 && g.turnOk(p.Pos.Y, p.Pos.X+1) - if b1 != b2 { - if b1 { - g.RunCh = 'h' - dx = -1 - } else { - g.RunCh = 'l' - dx = 1 - } + return + } + } - dy = 0 + if hitBound { + ch = ' ' // fall into the wall case below + } - g.turnref() + switch ch { + case ' ', '|', '-': + if turn, ndy, ndx := g.passageTurn(dy, dx); turn { + dy, dx = ndy, ndx - nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx} + g.turnRefresh() - goto over + nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx} + + continue // the C goto over: re-check the turned move + } + + g.Running = false + g.After = false + case Door: + g.Running = false + if g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Has(FPassage) { + g.enterRoom(nh) + } + + g.finishMove(nh, fl) + case Trap: + tr := g.springTrap(nh) + if tr == TrapDoor || tr == TrapTeleport { + return + } + + g.finishMove(nh, fl) + case Passage: + // when you're in a corridor, you don't know if you're in a maze + // room or not, and there ain't no way to find out if you're + // leaving a maze room, so it is necessary to always recalculate + // proom. + p.Room = g.roomIn(p.Pos) + g.finishMove(nh, fl) + case Floor: + if !fl.Has(FReal) { + g.springTrap(p.Pos) + } + + g.finishMove(nh, fl) + default: + if ch == Stairs { + g.SeenStairs = true + } + + g.Running = false + if isUpper(ch) || g.Level.MonsterAt(nh.Y, nh.X) != nil { + g.fight(nh, p.CurWeapon, false) + } else { + if ch != Stairs { + g.Take = ch } + + g.finishMove(nh, fl) } } - g.Running = false - g.After = false - case Door: - g.Running = false - if g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Has(FPassage) { - g.enterRoom(nh) - } - - g.moveStuff(nh, fl) - case Trap: - tr := g.beTrapped(nh) - if tr == TrapDoor || tr == TrapTeleport { - return - } - - g.moveStuff(nh, fl) - case Passage: - // when you're in a corridor, you don't know if you're in a maze - // room or not, and there ain't no way to find out if you're - // leaving a maze room, so it is necessary to always recalculate - // proom. - p.Room = g.roomin(p.Pos) - g.moveStuff(nh, fl) - case Floor: - if !fl.Has(FReal) { - g.beTrapped(p.Pos) - } - - g.moveStuff(nh, fl) - default: - if ch == Stairs { - g.SeenStairs = true - } - - g.Running = false - if isUpper(ch) || g.Level.MonsterAt(nh.Y, nh.X) != nil { - g.fight(nh, p.CurWeapon, false) - } else { - if ch != Stairs { - g.Take = ch - } - - g.moveStuff(nh, fl) - } + return } } -// moveStuff is the move_stuff label in do_move: complete the step. -func (g *RogueGame) moveStuff(nh Coord, fl PlaceFlags) { +// passageTurn checks whether a runner in a gone-room passage should turn +// the corner instead of stopping at a wall (the PASSGO block of move.c +// do_move). It reports whether to turn and the new deltas, updating RunCh. +func (g *RogueGame) passageTurn(dy, dx int) (bool, int, int) { + p := &g.Player + if !g.Options.PassGo || !g.Running || !p.Room.Flags.Has(Gone) || + p.On(Blind) { + return false, dy, dx + } + + var b1, b2 bool + + switch g.RunCh { + case 'h', 'l': + b1 = p.Pos.Y != 1 && g.turnOk(p.Pos.Y-1, p.Pos.X) + + b2 = p.Pos.Y != NumLines-2 && g.turnOk(p.Pos.Y+1, p.Pos.X) + if b1 != b2 { + if b1 { + g.RunCh = 'k' + dy = -1 + } else { + g.RunCh = 'j' + dy = 1 + } + + return true, dy, 0 + } + case 'j', 'k': + b1 = p.Pos.X != 0 && g.turnOk(p.Pos.Y, p.Pos.X-1) + + b2 = p.Pos.X != NumCols-1 && g.turnOk(p.Pos.Y, p.Pos.X+1) + if b1 != b2 { + if b1 { + g.RunCh = 'h' + dx = -1 + } else { + g.RunCh = 'l' + dx = 1 + } + + return true, 0, dx + } + } + + return false, dy, dx +} + +// finishMove is the move_stuff label in do_move: complete the step. +func (g *RogueGame) finishMove(nh Coord, fl PlaceFlags) { p := &g.Player g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt()) @@ -198,8 +210,9 @@ func (g *RogueGame) turnOk(y, x int) bool { return pp.Ch == Door || pp.Flags&(FReal|FPassage) == (FReal|FPassage) } -// turnref decides whether to refresh at a passage turning (move.c turnref). -func (g *RogueGame) turnref() { +// turnRefresh decides whether to refresh at a passage turning (move.c +// turnref). +func (g *RogueGame) turnRefresh() { p := &g.Player pp := g.Level.At(p.Pos.Y, p.Pos.X) @@ -228,8 +241,8 @@ func (g *RogueGame) doorOpen(rp *Room) { } } -// beTrapped makes him pay for stepping on a trap (move.c be_trapped). -func (g *RogueGame) beTrapped(tc Coord) TrapKind { +// springTrap makes him pay for stepping on a trap (move.c be_trapped). +func (g *RogueGame) springTrap(tc Coord) TrapKind { p := &g.Player if p.On(Levitating) { return TrapRust // anything that's not a door or teleport @@ -313,7 +326,7 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind { } if !p.IsWearing(RingSustainStrength) && !g.save(VsPoison) { - g.chgStr(-1) + g.changeStrength(-1) } g.msg("a small dart just hit you in the shoulder") @@ -328,9 +341,9 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind { return tr } -// rndmove moves in a random direction if the monster/person is confused -// (move.c rndmove). -func (g *RogueGame) rndmove(who *Creature) Coord { +// randomStep moves in a random direction if the monster/person is +// confused (move.c rndmove). +func (g *RogueGame) randomStep(who *Creature) Coord { ret := Coord{ Y: who.Pos.Y + g.rnd(3) - 1, X: who.Pos.X + g.rnd(3) - 1, diff --git a/game/newlevel.go b/game/newlevel.go index 590fffb..3d162de 100644 --- a/game/newlevel.go +++ b/game/newlevel.go @@ -27,8 +27,8 @@ func (g *RogueGame) NewLevel() { // go with them (the garbage collector is our free_list). g.Level.Monsters = nil g.Level.Objects = nil - g.doRooms() // Draw rooms - g.doPassages() // Draw passages + g.digRooms() // Draw rooms + g.digPassages() // Draw passages p.NoFood++ @@ -61,7 +61,7 @@ func (g *RogueGame) NewLevel() { g.SeenStairs = false for _, tp := range g.Level.Monsters { - tp.Room = g.roomin(tp.Pos) + tp.Room = g.roomIn(tp.Pos) } hero, _ := g.findFloor(true) @@ -78,8 +78,8 @@ func (g *RogueGame) NewLevel() { } } -// rndRoom picks a room that is really there (new_level.c rnd_room). -func (g *RogueGame) rndRoom() int { +// randomRoom picks a room that is really there (new_level.c rnd_room). +func (g *RogueGame) randomRoom() int { for { rm := g.rnd(MaxRooms) if !g.Level.Rooms[rm].Flags.Has(Gone) { @@ -98,7 +98,7 @@ func (g *RogueGame) putThings() { } // check for treasure rooms, and if so, put it in. if g.rnd(treasRoomChance) == 0 { - g.treasRoom() + g.treasureRoom() } // Do MAXOBJ attempts to put things on a level for range MaxObj { @@ -126,9 +126,9 @@ func (g *RogueGame) putThings() { } } -// treasRoom adds a treasure room (new_level.c treas_room). -func (g *RogueGame) treasRoom() { - rp := &g.Level.Rooms[g.rndRoom()] +// treasureRoom adds a treasure room (new_level.c treas_room). +func (g *RogueGame) treasureRoom() { + rp := &g.Level.Rooms[g.randomRoom()] spots := min((rp.Max.Y-2)*(rp.Max.X-2)-minTreas, maxTreas-minTreas) diff --git a/game/passages.go b/game/passages.go index 39dc04b..a49682f 100644 --- a/game/passages.go +++ b/game/passages.go @@ -2,8 +2,8 @@ package game // passages.c — draw the connecting passages. -// doPassages draws all the passages on a level (passages.c do_passages). -func (g *RogueGame) doPassages() { +// digPassages draws all the passages on a level (passages.c do_passages). +func (g *RogueGame) digPassages() { var ( isconn [MaxRooms][MaxRooms]bool ingraph [MaxRooms]bool @@ -41,7 +41,7 @@ func (g *RogueGame) doPassages() { // otherwise, connect new room to the graph, and draw a tunnel // to it ingraph[r2] = true //nolint:gosec // G602: rnd(MaxRooms) bounded - g.conn(r1, r2) + g.connectRooms(r1, r2) isconn[r1][r2] = true isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded roomcount++ @@ -69,18 +69,18 @@ func (g *RogueGame) doPassages() { } // if there is one, connect it and look for the next added passage if j != 0 { - g.conn(r1, r2) + g.connectRooms(r1, r2) isconn[r1][r2] = true isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded } } - g.passnum() + g.numberPassages() } -// conn draws a corridor from a room in a certain direction (passages.c -// conn). -func (g *RogueGame) conn(r1, r2 int) { +// connectRooms draws a corridor from a room in a certain direction +// (passages.c conn). +func (g *RogueGame) connectRooms(r1, r2 int) { var ( rm int direc byte @@ -193,13 +193,13 @@ func (g *RogueGame) conn(r1, r2 int) { if !rpf.Flags.Has(Gone) { g.door(rpf, spos) } else { - g.putpass(spos) + g.putPassage(spos) } if !rpt.Flags.Has(Gone) { g.door(rpt, epos) } else { - g.putpass(epos) + g.putPassage(epos) } // Get ready to move... curr := spos @@ -210,13 +210,13 @@ func (g *RogueGame) conn(r1, r2 int) { // Check if we are at the turn place, if so do the turn if distance == turnSpot { for ; turnDistance > 0; turnDistance-- { - g.putpass(curr) + g.putPassage(curr) curr.X += turnDelta.X curr.Y += turnDelta.Y } } // Continue digging along - g.putpass(curr) + g.putPassage(curr) distance-- } @@ -229,9 +229,9 @@ func (g *RogueGame) conn(r1, r2 int) { } } -// putpass adds a passage character or secret passage here (passages.c +// putPassage adds a passage character or secret passage here (passages.c // putpass). -func (g *RogueGame) putpass(cp Coord) { +func (g *RogueGame) putPassage(cp Coord) { pp := g.Level.At(cp.Y, cp.X) pp.Flags.Set(FPassage) @@ -302,8 +302,8 @@ func (g *RogueGame) addPass() { } } -// passnum assigns a number to each passageway (passages.c passnum). -func (g *RogueGame) passnum() { +// numberPassages assigns a number to each passageway (passages.c passnum). +func (g *RogueGame) numberPassages() { g.pnum = 0 g.newpnum = false @@ -315,14 +315,14 @@ func (g *RogueGame) passnum() { rp := &g.Level.Rooms[i] for j := range rp.Exits { g.newpnum = true - g.numpass(rp.Exits[j].Y, rp.Exits[j].X) + g.numberPassage(rp.Exits[j].Y, rp.Exits[j].X) } } } -// numpass numbers a passageway square and its brethren (passages.c +// numberPassage numbers a passageway square and its brethren (passages.c // numpass). -func (g *RogueGame) numpass(y, x int) { +func (g *RogueGame) numberPassage(y, x int) { if x >= NumCols || x < 0 || y >= NumLines || y <= 0 { return } @@ -348,10 +348,10 @@ func (g *RogueGame) numpass(y, x int) { *fp |= PlaceFlags(g.pnum) //nolint:gosec // G115: pnum < MaxPass=13 // recurse on the surrounding places - g.numpass(y+1, x) - g.numpass(y-1, x) - g.numpass(y, x+1) - g.numpass(y, x-1) + g.numberPassage(y+1, x) + g.numberPassage(y-1, x) + g.numberPassage(y, x+1) + g.numberPassage(y, x-1) } // abs is C abs() for ints. diff --git a/game/potions.go b/game/potions.go index f1ba406..64bf973 100644 --- a/game/potions.go +++ b/game/potions.go @@ -49,7 +49,7 @@ func (g *RogueGame) quaff() { if p.IsWearing(RingSustainStrength) { g.msg("you feel momentarily sick") } else { - g.chgStr(-(g.rnd(3) + 1)) + g.changeStrength(-(g.rnd(3) + 1)) g.msg("you feel very sick now") g.comeDown(0) } @@ -64,7 +64,7 @@ func (g *RogueGame) quaff() { g.msg("you begin to feel better") case PotionGainStrength: g.Items.Potions[PotionGainStrength].Know = true - g.chgStr(1) + g.changeStrength(1) g.msg("you feel stronger, now. What bulging muscles!") case PotionDetectMonsters: p.Flags.Set(SenseMonsters) diff --git a/game/rings.go b/game/rings.go index a43ff93..de988ae 100644 --- a/game/rings.go +++ b/game/rings.go @@ -54,7 +54,7 @@ func (g *RogueGame) ringOn() { // Calculate the effect it has on the poor guy. switch obj.RingKind() { case RingAddStrength: - g.chgStr(obj.Bonus) + g.changeStrength(obj.Bonus) case RingSeeInvisible: g.invisOn() case RingAggravateMonsters: diff --git a/game/rooms.go b/game/rooms.go index 704cc36..d78a5cf 100644 --- a/game/rooms.go +++ b/game/rooms.go @@ -17,9 +17,9 @@ type mazeState struct { const goldGrp = 1 -// doRooms creates rooms and corridors with a connectivity graph (rooms.c +// digRooms creates rooms and corridors with a connectivity graph (rooms.c // do_rooms). -func (g *RogueGame) doRooms() { +func (g *RogueGame) digRooms() { var bsze Coord // maximum room size bsze.X = NumCols / 3 @@ -34,7 +34,7 @@ func (g *RogueGame) doRooms() { // Put the gone rooms, if any, on the level leftOut := g.rnd(4) for range leftOut { - g.Level.Rooms[g.rndRoom()].Flags.Set(Gone) + g.Level.Rooms[g.randomRoom()].Flags.Set(Gone) } // dig and populate all the rooms on the level for i := range g.Level.Rooms { @@ -126,7 +126,7 @@ func (g *RogueGame) doRooms() { // rooms; for maze rooms, draws the maze (rooms.c draw_room). func (g *RogueGame) drawRoom(rp *Room) { if rp.Flags.Has(Maze) { - g.doMaze(rp) + g.digMaze(rp) return } @@ -158,8 +158,8 @@ func (g *RogueGame) horiz(rp *Room, starty int) { } } -// doMaze digs a maze (rooms.c do_maze). -func (g *RogueGame) doMaze(rp *Room) { +// digMaze digs a maze (rooms.c do_maze). +func (g *RogueGame) digMaze(rp *Room) { m := &g.maze for y := range m.maze { for x := range m.maze[y] { @@ -175,7 +175,7 @@ func (g *RogueGame) doMaze(rp *Room) { starty := (g.rnd(rp.Max.Y) / 2) * 2 startx := (g.rnd(rp.Max.X) / 2) * 2 pos := Coord{Y: starty + m.starty, X: startx + m.startx} - g.putpass(pos) + g.putPassage(pos) g.dig(starty, startx) } @@ -211,8 +211,8 @@ func (g *RogueGame) dig(y, x int) { return } - g.accntMaze(y, x, nexty, nextx) - g.accntMaze(nexty, nextx, y, x) + g.accountMaze(y, x, nexty, nextx) + g.accountMaze(nexty, nextx, y, x) var pos Coord if nexty == y { @@ -231,16 +231,16 @@ func (g *RogueGame) dig(y, x int) { } } - g.putpass(pos) + g.putPassage(pos) pos.Y = nexty + m.starty pos.X = nextx + m.startx - g.putpass(pos) + g.putPassage(pos) g.dig(nexty, nextx) } } -// accntMaze accounts for maze exits (rooms.c accnt_maze). -func (g *RogueGame) accntMaze(y, x, ny, nx int) { +// accountMaze accounts for maze exits (rooms.c accnt_maze). +func (g *RogueGame) accountMaze(y, x, ny, nx int) { sp := &g.maze.maze[y][x] for i := range sp.nexits { if sp.exits[i].Y == ny && sp.exits[i].X == nx { @@ -255,8 +255,8 @@ func (g *RogueGame) accntMaze(y, x, ny, nx int) { } } -// rndPos picks a random spot in a room (rooms.c rnd_pos). -func (g *RogueGame) rndPos(rp *Room) Coord { +// randomPos picks a random spot in a room (rooms.c rnd_pos). +func (g *RogueGame) randomPos(rp *Room) Coord { var cp Coord cp.X = rp.Pos.X + g.rnd(rp.Max.X-2) + 1 @@ -296,7 +296,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co } if pickroom { - rp = &g.Level.Rooms[g.rndRoom()] + rp = &g.Level.Rooms[g.randomRoom()] compchar = Floor if rp.Flags.Has(Maze) { @@ -304,7 +304,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co } } - cp := g.rndPos(rp) + cp := g.randomPos(rp) pp := g.Level.At(cp.Y, cp.X) if monst { @@ -321,7 +321,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co // enter_room). func (g *RogueGame) enterRoom(cp Coord) { p := &g.Player - rp := g.roomin(cp) + rp := g.roomIn(cp) p.Room = rp g.doorOpen(rp) diff --git a/game/save.go b/game/save.go index 193333b..5452d79 100644 --- a/game/save.go +++ b/game/save.go @@ -449,99 +449,102 @@ func (g *RogueGame) applySnapshot(st *SaveState) { g.scr.Std.SetContents(st.Screen) } -// saveGame implements the "save game" command (save.c save_game). The C -// goto over/gotfile flow becomes the useDefault flag. +// saveGame implements the "save game" command (save.c save_game). The +// labeled prompt loop and the useDefault flag stand in for the C +// goto over/gotfile flow. func (g *RogueGame) saveGame() { g.Msgs.Mpos = 0 -over: - useDefault := false - - if g.FileName != "" { - var c byte - - for { - g.msg("save file (%s)? ", g.FileName) - c = g.readchar() - - g.Msgs.Mpos = 0 - if c == Escape { - g.msg("") - - return - } - - if c == 'n' || c == 'N' || c == 'y' || c == 'Y' { - break - } - - g.msg("please answer Y or N") - } - - if c == 'y' || c == 'Y' { - g.addstr("Yes\n") - g.refresh() - - useDefault = true - } - } - +prompt: for { - var buf string - if useDefault { - buf = g.FileName - useDefault = false - } else { - g.Msgs.Mpos = 0 - g.msg("file name: ") + useDefault := false - if g.getStr(&buf, g.scr.Std) == Quit { - g.msg("") + if g.FileName != "" { + var c byte - return - } - - g.Msgs.Mpos = 0 - } - // test to see if the file exists - _, statErr := os.Stat(buf) - if statErr == nil { for { - g.msg("File exists. Do you wish to overwrite it?") - g.Msgs.Mpos = 0 + g.msg("save file (%s)? ", g.FileName) + c = g.readchar() - c := g.readchar() + g.Msgs.Mpos = 0 if c == Escape { g.msg("") return } - if c == 'y' || c == 'Y' { + if c == 'n' || c == 'N' || c == 'y' || c == 'Y' { break } - if c == 'n' || c == 'N' { - goto over - } - - g.msg("Please answer Y or N") + g.msg("please answer Y or N") } - g.msg("file name: %s", buf) - _ = os.Remove(g.FileName) // best effort, as in C (md_unlink) + if c == 'y' || c == 'Y' { + g.addstr("Yes\n") + g.refresh() + + useDefault = true + } } - g.FileName = buf + for { + var buf string + if useDefault { + buf = g.FileName + useDefault = false + } else { + g.Msgs.Mpos = 0 + g.msg("file name: ") - err := g.saveFile(g.FileName) - if err != nil { - g.msg("%s", err.Error()) + if g.getStr(&buf, g.scr.Std) == Quit { + g.msg("") - continue + return + } + + g.Msgs.Mpos = 0 + } + // test to see if the file exists + _, statErr := os.Stat(buf) + if statErr == nil { + for { + g.msg("File exists. Do you wish to overwrite it?") + g.Msgs.Mpos = 0 + + c := g.readchar() + if c == Escape { + g.msg("") + + return + } + + if c == 'y' || c == 'Y' { + break + } + + if c == 'n' || c == 'N' { + continue prompt // the C goto over: start again + } + + g.msg("Please answer Y or N") + } + + g.msg("file name: %s", buf) + _ = os.Remove(g.FileName) // best effort, as in C (md_unlink) + } + + g.FileName = buf + + err := g.saveFile(g.FileName) + if err != nil { + g.msg("%s", err.Error()) + + continue + } + + break prompt } - - break } g.myExit() diff --git a/game/sticks.go b/game/sticks.go index 835c2b1..20ac1d6 100644 --- a/game/sticks.go +++ b/game/sticks.go @@ -80,7 +80,7 @@ func (g *RogueGame) doZap() { case WandInvisibility: tp.Flags.Set(Invisible) - if g.cansee(y, x) { + if g.canSee(y, x) { g.mvaddch(y, x, tp.OldCh) } case WandPolymorph: @@ -186,7 +186,7 @@ func (g *RogueGame) doZap() { g.Delta.Y = y g.Delta.X = x - g.runto(g.Delta) + g.runTo(g.Delta) } case WandLightning, WandFire, WandCold: var name string @@ -243,7 +243,7 @@ func (g *RogueGame) drain() { if mp.Stats.HP -= cnt; mp.Stats.HP <= 0 { g.killed(mp, g.seeMonst(mp)) } else { - g.runto(mp.Pos) + g.runTo(mp.Pos) } } } @@ -339,7 +339,7 @@ func (g *RogueGame) fireBolt(start Coord, dir *Coord, name string) { } } else if ch != 'M' || tp.Disguise == 'M' { if fromHero { - g.runto(pos) + g.runTo(pos) } if g.Options.Terse { diff --git a/game/things.go b/game/things.go index 0237bf1..a74173f 100644 --- a/game/things.go +++ b/game/things.go @@ -201,7 +201,7 @@ func (g *RogueGame) dropCheck(obj *Object) bool { switch obj.RingKind() { case RingAddStrength: - g.chgStr(-obj.Bonus) + g.changeStrength(-obj.Bonus) case RingSeeInvisible: g.unsee(0) g.Extinguish(DUnsee) diff --git a/game/weapons.go b/game/weapons.go index daa9d61..8158a51 100644 --- a/game/weapons.go +++ b/game/weapons.go @@ -36,7 +36,7 @@ func (g *RogueGame) doMotion(obj *Object, ydelta, xdelta int) { obj.Pos = p.Pos for { // Erase the old one - if obj.Pos != p.Pos && g.cansee(obj.Pos.Y, obj.Pos.X) && !g.Options.Terse { + if obj.Pos != p.Pos && g.canSee(obj.Pos.Y, obj.Pos.X) && !g.Options.Terse { ch := g.Level.Char(obj.Pos.Y, obj.Pos.X) if ch == Floor && !g.showFloor() { ch = ' ' @@ -51,7 +51,7 @@ func (g *RogueGame) doMotion(obj *Object, ydelta, xdelta int) { ch := g.Level.VisibleChar(obj.Pos.Y, obj.Pos.X) if stepOk(ch) && ch != Door { // It hasn't hit anything yet, so display it if it's alright. - if g.cansee(obj.Pos.Y, obj.Pos.X) && !g.Options.Terse { + if g.canSee(obj.Pos.Y, obj.Pos.X) && !g.Options.Terse { g.mvaddch(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph()) g.refresh() } @@ -70,7 +70,7 @@ func (g *RogueGame) fall(obj *Object, pr bool) { pp.Ch = obj.Kind.Glyph() obj.Pos = fpos - if g.cansee(fpos.Y, fpos.X) { + if g.canSee(fpos.Y, fpos.X) { if pp.Monst != nil { pp.Monst.OldCh = obj.Kind.Glyph() } else { diff --git a/game/wizard.go b/game/wizard.go index 9688d48..dedb1f4 100644 --- a/game/wizard.go +++ b/game/wizard.go @@ -181,7 +181,7 @@ func (g *RogueGame) teleport() { g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt()) c, _ := g.findFloor(true) - if g.roomin(c) != p.Room { + if g.roomIn(c) != p.Room { g.leaveRoom(p.Pos) p.Pos = c g.enterRoom(p.Pos)