12 Commits

Author SHA1 Message Date
acef593288 Merge refactor/god-object-extraction (refactor step 6) 2026-07-07 02:42:18 +02:00
0b56ac8019 Extract MessageLine, Player pack ops, and Level list management
Refactor step 6. MessageLine (was MsgLine) owns the msg/addmsg/endmsg
machinery, wired to its screen, pre---More-- redraw, and input via
attach(); RogueGame keeps one-line msg/addmsgf/endmsg shorthands so
the ~400 call sites are unchanged. Player gains nextPackChar and
removeFromPack (the state half of pack.c leave_pack); leavePack keeps
only the LastPick repeat-command tracking. Level gains ObjectAt
(misc.c find_obj) and AddObject/RemoveObject/AddMonster/RemoveMonster,
replacing direct attach/detach calls on the level lists. Inventory and
pickup UI flows stay on RogueGame: display and orchestration, not
state surgery. Behavior and RNG order unchanged; suite green.
2026-07-07 02:42:18 +02:00
a094f7c6c3 Merge refactor/fix-nonamedreturns 2026-07-07 02:35:18 +02:00
0caaa14198 Drop unused named return on moveMonster (nonamedreturns) 2026-07-07 02:35:18 +02:00
d3ef07cfa7 Merge refactor/item-combat-ui-renames (refactor step 5) 2026-07-07 02:34:32 +02:00
f432c8718c Rename getDir to promptDirection; rotate TODO (step 5 done) 2026-07-07 02:34:32 +02:00
ae79fd5e84 Rename combat methods; -1 status codes become named bool results
Refactor step 5, combat: rollEm→rollAttacks; attack, moveMonster, and
chaseStep return (removed bool) instead of the C -1/0 int codes.
Behavior unchanged; suite green.
2026-07-07 02:33:15 +02:00
6d798c56ed Rename item-subsystem methods (refactor step 5, items)
getItem→promptPackItem now returns (obj, ok) instead of a nil-signaling
pointer; invName→inventoryName; doPot→applyPotionFuse. C breadcrumbs
kept. Behavior unchanged; suite green.
2026-07-07 02:31:38 +02:00
0554f5d4f1 Merge refactor/movement-renames (refactor step 4) 2026-07-07 02:29:06 +02:00
6850c87ae7 Rename movement/world methods to idiomatic Go; remove all gotos
Refactor step 4. Renames (C breadcrumbs kept in doc comments):
doMove→moveHero, beTrapped→springTrap, rndmove→randomStep,
doRooms→digRooms, doPassages→digPassages, doMaze→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 are gone: moveHero uses a retry loop with the
PASSGO corner logic extracted into passageTurn; dispatch re-dispatches
via a loop; chaseStep re-checks via a loop; saveGame uses a labeled
prompt loop. Control flow and RNG call order are unchanged; suite
green.
2026-07-07 02:29:06 +02:00
65a1cd68b8 Merge refactor/drop-unused-nolints 2026-07-07 02:17:25 +02:00
525465a68b Drop two unused gosec nolint directives in chooseSeed
The 0x7fffffff mask makes both int32 conversions provably safe, so
G115 never fired; nolintlint flags the directives as unused.
2026-07-07 02:17:25 +02:00
30 changed files with 903 additions and 805 deletions

70
TODO.md
View File

@@ -29,14 +29,44 @@ Refactor ground rules:
# Next Step # Next Step
Refactor step 4: method renames, movement/world subsystem Refactor step 7: effects dispatch — the giant quaff/readScroll/doZap
(doMove→moveHero, beTrapped→springTrap, rndmove→randomStep, switches become per-kind handler tables of small named methods,
doRooms/doPassages/doMaze→digRooms/digPassages/digMaze, keeping effect order and RNG call sequence identical. This step also
chgStr→changeStrength, ...); remove the goto/label flows in doMove, clears the outstanding cyclop/gocognit/nestif lint findings.
dispatch, and saveGame in favor of loops and helpers.
# Completed Steps # Completed Steps
- 2026-07-07 Refactor step 6 (refactor/god-object-extraction):
MessageLine (was MsgLine) owns the msg/addmsg/endmsg machinery,
wired to its screen/look/input needs via attach(); RogueGame keeps
one-line msg/addmsgf/endmsg shorthands so call sites are unchanged.
Player owns pack bookkeeping (nextPackChar, removeFromPack — the
state half of leave_pack; leavePack keeps only LastPick tracking).
Level owns object/monster list management and lookup (ObjectAt
replaces findObj; AddObject/RemoveObject/AddMonster/RemoveMonster
replace direct attachObj/detachObj/attachMon/detachMon on level
lists). Inventory/pickup UI flows stay on RogueGame deliberately:
they are display and turn orchestration, not state surgery.
- 2026-07-07 Refactor step 5 (refactor/item-combat-ui-renames, three
commits, one subsystem each): items — getItem→promptPackItem now
returning (obj, ok), invName→inventoryName, doPot→applyPotionFuse;
combat — rollEm→rollAttacks, attack/moveMonster/chaseStep return
(removed bool) instead of C -1/0 int codes; UI —
getDir→promptDirection. C breadcrumbs kept; suite green.
- 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 - 2026-07-07 Lint adoption finished (refactor/no-package-globals): all
37 package-level vars moved into `gameData` (built by `newGameData`, 37 package-level vars moved into `gameData` (built by `newGameData`,
hung on RogueGame as `g.data`, set in NewGame and Restore); ObjectKind hung on RogueGame as `g.data`, set in NewGame and Restore); ObjectKind
@@ -98,41 +128,27 @@ dispatch, and saveGame in favor of loops and helpers.
# Future Steps # Future Steps
1. Refactor step 5: method renames, items/combat/UI subsystems 1. Refactor step 8: constructor and style pass per the house
(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
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
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
styleguide — game.New(game.Params{...}) replacing NewGame(Config); styleguide — game.New(game.Params{...}) replacing NewGame(Config);
replace the gameEnd panic unwind with error-based turn results where replace the gameEnd panic unwind with error-based turn results where
feasible; 77-column wrap sweep. feasible; 77-column wrap sweep.
5. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the 2. Docs refresh: update ARCHITECTURE.md Part 2 and README.md for the
post-refactor names; add the C name → Go name rename table. post-refactor names; add the C name → Go name rename table.
6. Playtest hardening pass: play several full games with the tcell 3. Playtest hardening pass: play several full games with the tcell
binary and extend run_test.go to script a deeper multi-level binary and extend run_test.go to script a deeper multi-level
playthrough (descend past level 5, use potions, scrolls, zapping, playthrough (descend past level 5, use potions, scrolls, zapping,
save/restore). Fix any panics, message mismatches, or divergences save/restore). Fix any panics, message mismatches, or divergences
from the C behavior that this uncovers, with regression tests. from the C behavior that this uncovers, with regression tests.
7. Verify the seed-compatibility claim against the C reference on 4. Verify the seed-compatibility claim against the C reference on
c-master: same seed, same dungeon, same item tables, for several c-master: same seed, same dungeon, same item tables, for several
seeds. seeds.
8. Broaden unit test coverage where playtesting finds thin spots 5. Broaden unit test coverage where playtesting finds thin spots
(rings, sticks, wizard commands). (rings, sticks, wizard commands).
9. Tag a release once a full game (Amulet retrieval and score entry) 6. Tag a release once a full game (Amulet retrieval and score entry)
completes without defects. completes without defects.
10. Full-terminal-size support (deferred by explicit decision 7. Full-terminal-size support (deferred by explicit decision
2026-07-06): per-game dungeon dimensions instead of the 80x24 2026-07-06): per-game dungeon dimensions instead of the 80x24
constants; open design questions are resize policy, gameplay constants; open design questions are resize policy, gameplay
tuning at larger sizes, and a --classic 80x24 mode. tuning at larger sizes, and a --classic 80x24 mode.
11. Note: this repo is exempt from the standard policy scaffold. Do not 8. Note: this repo is exempt from the standard policy scaffold. Do not
add Makefile, Dockerfile, or REPO_POLICIES.md. add Makefile, Dockerfile, or REPO_POLICIES.md.

View File

@@ -133,6 +133,6 @@ func chooseSeed(wizard bool) int32 {
// The C game computed `lowtime + getpid()` in int; the truncation to // The C game computed `lowtime + getpid()` in int; the truncation to
// 32 bits is the same wraparound the C int arithmetic performed. // 32 bits is the same wraparound the C int arithmetic performed.
return int32(time.Now().Unix()&0x7fffffff) + //nolint:gosec // G115: deliberate wrap return int32(time.Now().Unix()&0x7fffffff) +
int32(os.Getpid()&0x7fffffff) //nolint:gosec // G115: deliberate wrap int32(os.Getpid()&0x7fffffff)
} }

View File

@@ -6,8 +6,8 @@ package game
func (g *RogueGame) wear() { func (g *RogueGame) wear() {
p := &g.Player p := &g.Player
obj := g.getItem("wear", KindArmor) obj, ok := g.promptPackItem("wear", KindArmor)
if obj == nil { if !ok {
return return
} }
@@ -32,7 +32,7 @@ func (g *RogueGame) wear() {
g.wasteTime() g.wasteTime()
obj.Flags.Set(Known) obj.Flags.Set(Known)
sp := g.invName(obj, true) sp := g.inventoryName(obj, true)
p.CurArmor = obj p.CurArmor = obj
if !g.Options.Terse { if !g.Options.Terse {
@@ -70,7 +70,7 @@ func (g *RogueGame) takeOff() {
g.addmsgf("you used to be") g.addmsgf("you used to be")
} }
g.msg(" wearing %c) %s", obj.PackCh, g.invName(obj, true)) g.msg(" wearing %c) %s", obj.PackCh, g.inventoryName(obj, true))
} }
// wasteTime does nothing but let other things happen (armor.c waste_time). // wasteTime does nothing but let other things happen (armor.c waste_time).

View File

@@ -13,12 +13,12 @@ func (g *RogueGame) runners(int) {
origPos := tp.Pos origPos := tp.Pos
wastarget := tp.On(Targeted) wastarget := tp.On(Targeted)
if g.moveMonst(tp) == -1 { if removed := g.moveMonster(tp); removed {
continue continue
} }
if tp.On(Flying) && distCp(g.Player.Pos, tp.Pos) >= 3 { if tp.On(Flying) && distCp(g.Player.Pos, tp.Pos) >= 3 {
if g.moveMonst(tp) == -1 { if removed := g.moveMonster(tp); removed {
continue continue
} }
} }
@@ -37,24 +37,25 @@ 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. // move_monst). The result reports that the monster died or left the
func (g *RogueGame) moveMonst(tp *Monster) int { // level (the C -1 return).
func (g *RogueGame) moveMonster(tp *Monster) bool {
if !tp.On(Slowed) || tp.Turn { if !tp.On(Slowed) || tp.Turn {
if g.doChase(tp) == -1 { if g.chaseStep(tp) {
return -1 return true
} }
} }
if tp.On(Hasted) { if tp.On(Hasted) {
if g.doChase(tp) == -1 { if g.chaseStep(tp) {
return -1 return true
} }
} }
tp.Turn = !tp.Turn tp.Turn = !tp.Turn
return 0 return false
} }
// relocate makes the monster's new location be the specified one, updating // relocate makes the monster's new location be the specified one, updating
@@ -62,8 +63,8 @@ func (g *RogueGame) moveMonst(tp *Monster) int {
func (g *RogueGame) relocate(th *Monster, newLoc Coord) { func (g *RogueGame) relocate(th *Monster, newLoc Coord) {
if newLoc != th.Pos { if newLoc != th.Pos {
g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh) g.mvaddch(th.Pos.Y, th.Pos.X, th.OldCh)
th.Room = g.roomin(newLoc) th.Room = g.roomIn(newLoc)
g.setOldch(th, newLoc) g.setOldChar(th, newLoc)
oroom := th.Room oroom := th.Room
g.Level.SetMonsterAt(th.Pos.Y, th.Pos.X, nil) g.Level.SetMonsterAt(th.Pos.Y, th.Pos.X, nil)
@@ -86,9 +87,10 @@ func (g *RogueGame) relocate(th *Monster, newLoc Coord) {
} }
} }
// doChase makes one thing chase another (chase.c do_chase). Returns -1 if // chaseStep makes one thing chase another (chase.c do_chase). removed
// the chaser died in the attempt. // reports that the chaser died or left the level in the attempt (the C
func (g *RogueGame) doChase(th *Monster) int { // -1 return).
func (g *RogueGame) chaseStep(th *Monster) (removed bool) {
p := &g.Player p := &g.Player
stoprun := false // true means we are there stoprun := false // true means we are there
mindist := 32767 mindist := 32767
@@ -102,16 +104,16 @@ func (g *RogueGame) doChase(th *Monster) int {
if th.Dest == &p.Pos { if th.Dest == &p.Pos {
ree = p.Room ree = p.Room
} else { } else {
ree = g.roomin(*th.Dest) ree = g.roomIn(*th.Dest)
} }
// We don't count doors as inside rooms for this routine // We don't count doors as inside rooms for this routine
door := g.Level.Char(th.Pos.Y, th.Pos.X) == Door door := g.Level.Char(th.Pos.Y, th.Pos.X) == Door
var this Coord var this Coord
over: for {
// If the object of our desire is in a different room, and we are not // If the object of our desire is in a different room, and we are
// in a corridor, run to the door nearest to our goal. // not in a corridor, run to the door nearest to our goal.
if rer != ree { if rer != ree {
for i := range rer.Exits { for i := range rer.Exits {
curdist := distCp(*th.Dest, rer.Exits[i]) curdist := distCp(*th.Dest, rer.Exits[i])
@@ -125,13 +127,13 @@ over:
rer = &g.Level.Passages[*g.Level.FlagsAt(th.Pos.Y, th.Pos.X)&FPassNum] rer = &g.Level.Passages[*g.Level.FlagsAt(th.Pos.Y, th.Pos.X)&FPassNum]
door = false door = false
goto over continue // the C goto over: redo with the passage as room
} }
} else { } else {
this = *th.Dest this = *th.Dest
// For dragons check and see if (a) the hero is on a straight line // For dragons check and see if (a) the hero is on a straight
// from it, and (b) that it is within shooting distance, but // line from it, and (b) that it is within shooting distance,
// outside of striking range. // but outside of striking range.
if th.Type == 'D' && (th.Pos.Y == p.Pos.Y || th.Pos.X == p.Pos.X || 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)) && abs(th.Pos.Y-p.Pos.Y) == abs(th.Pos.X-p.Pos.X)) &&
distCp(th.Pos, p.Pos) <= BoltLength*BoltLength && distCp(th.Pos, p.Pos) <= BoltLength*BoltLength &&
@@ -153,9 +155,12 @@ over:
g.Kamikaze = false g.Kamikaze = false
} }
return 0 return false
} }
} }
break
}
// This now contains what we want to run to this time so we run to it. // 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 // If we hit it we either want to fight it or stop running
if !g.chase(th, this) { if !g.chase(th, this) {
@@ -164,7 +169,7 @@ over:
} else if this == *th.Dest { } else if this == *th.Dest {
for _, obj := range g.Level.Objects { for _, obj := range g.Level.Objects {
if th.Dest == &obj.Pos { if th.Dest == &obj.Pos {
detachObj(&g.Level.Objects, obj) g.Level.RemoveObject(obj)
attachObj(&th.Pack, obj) attachObj(&th.Pack, obj)
if th.Room.Flags.Has(Gone) { if th.Room.Flags.Has(Gone) {
@@ -185,7 +190,7 @@ over:
} }
} else { } else {
if th.Type == 'F' { if th.Type == 'F' {
return 0 return false
} }
} }
@@ -195,7 +200,7 @@ over:
th.Flags.Clear(Awake) th.Flags.Clear(Awake)
} }
return 0 return false
} }
// chase finds the spot for the chaser to move closer to the chasee // chase finds the spot for the chaser to move closer to the chasee
@@ -214,7 +219,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) || if (tp.On(Confused) && g.rnd(5) != 0) || (tp.Type == 'P' && g.rnd(5) == 0) ||
(tp.Type == 'B' && g.rnd(2) == 0) { (tp.Type == 'B' && g.rnd(2) == 0) {
// get a valid random move // get a valid random move
g.chRet = g.rndmove(&tp.Creature) g.chRet = g.randomStep(&tp.Creature)
curdist = distCp(g.chRet, ee) curdist = distCp(g.chRet, ee)
// Small chance that it will become un-confused // Small chance that it will become un-confused
if g.rnd(20) == 0 { if g.rnd(20) == 0 {
@@ -294,8 +299,8 @@ func (g *RogueGame) chase(tp *Monster, ee Coord) bool {
return curdist != 0 && g.chRet != p.Pos return curdist != 0 && g.chRet != p.Pos
} }
// setOldch sets the oldch character for the monster (chase.c set_oldch). // setOldChar sets the oldch character for the monster (chase.c set_oldch).
func (g *RogueGame) setOldch(tp *Monster, cp Coord) { func (g *RogueGame) setOldChar(tp *Monster, cp Coord) {
if tp.Pos == cp { if tp.Pos == cp {
return return
} }
@@ -341,8 +346,8 @@ func (g *RogueGame) seeMonst(mp *Monster) bool {
return !mp.Room.Flags.Has(Dark) return !mp.Room.Flags.Has(Dark)
} }
// runto sets a monster running after the hero (chase.c runto). // runTo sets a monster running after the hero (chase.c runto).
func (g *RogueGame) runto(runner Coord) { func (g *RogueGame) runTo(runner Coord) {
tp := g.Level.MonsterAt(runner.Y, runner.X) tp := g.Level.MonsterAt(runner.Y, runner.X)
if tp == nil { if tp == nil {
return return
@@ -353,9 +358,9 @@ func (g *RogueGame) runto(runner Coord) {
tp.Dest = g.findDest(tp) 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). // 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) fp := *g.Level.FlagsAt(cp.Y, cp.X)
if fp.Has(FPassage) { if fp.Has(FPassage) {
return &g.Level.Passages[fp&FPassNum] return &g.Level.Passages[fp&FPassNum]
@@ -387,9 +392,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)) 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). // cansee).
func (g *RogueGame) cansee(y, x int) bool { func (g *RogueGame) canSee(y, x int) bool {
p := &g.Player p := &g.Player
if p.On(Blind) { if p.On(Blind) {
return false return false
@@ -408,7 +413,7 @@ func (g *RogueGame) cansee(y, x int) bool {
} }
// We can only see if the hero is in the same room as the coordinate // 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. // 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) return rer == p.Room && !rer.Flags.Has(Dark)
} }
@@ -426,7 +431,7 @@ func (g *RogueGame) findDest(tp *Monster) *Coord {
continue 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 claimed := false
for _, other := range g.Level.Monsters { for _, other := range g.Level.Monsters {

View File

@@ -151,12 +151,12 @@ func (g *RogueGame) command() {
} }
} }
// dispatch runs one command character (the big switch in command.c, // dispatch runs one command character (the big switch in command.c; the
// including its `goto over` re-dispatch). // loop stands in for its `goto over` re-dispatch).
func (g *RogueGame) dispatch(ch byte) { func (g *RogueGame) dispatch(ch byte) {
p := &g.Player p := &g.Player
over: for {
switch ch { switch ch {
case ',': case ',':
var found *Object var found *Object
@@ -189,37 +189,37 @@ over:
case '!': case '!':
g.shell() g.shell()
case 'h': case 'h':
g.doMove(0, -1) g.moveHero(0, -1)
case 'j': case 'j':
g.doMove(1, 0) g.moveHero(1, 0)
case 'k': case 'k':
g.doMove(-1, 0) g.moveHero(-1, 0)
case 'l': case 'l':
g.doMove(0, 1) g.moveHero(0, 1)
case 'y': case 'y':
g.doMove(-1, -1) g.moveHero(-1, -1)
case 'u': case 'u':
g.doMove(-1, 1) g.moveHero(-1, 1)
case 'b': case 'b':
g.doMove(1, -1) g.moveHero(1, -1)
case 'n': case 'n':
g.doMove(1, 1) g.moveHero(1, 1)
case 'H': case 'H':
g.doRun('h') g.startRun('h')
case 'J': case 'J':
g.doRun('j') g.startRun('j')
case 'K': case 'K':
g.doRun('k') g.startRun('k')
case 'L': case 'L':
g.doRun('l') g.startRun('l')
case 'Y': case 'Y':
g.doRun('y') g.startRun('y')
case 'U': case 'U':
g.doRun('u') g.startRun('u')
case 'B': case 'B':
g.doRun('b') g.startRun('b')
case 'N': case 'N':
g.doRun('n') g.startRun('n')
case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'), case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'),
CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'): CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'):
if !p.On(Blind) { if !p.On(Blind) {
@@ -234,13 +234,13 @@ over:
g.direction = ch g.direction = ch
} }
goto over continue // the C goto over: re-dispatch as the run command
case 'F', 'f': case 'F', 'f':
if ch == 'F' { if ch == 'F' {
g.Kamikaze = true g.Kamikaze = true
} }
if !g.getDir() { if !g.promptDirection() {
g.After = false g.After = false
break break
@@ -266,10 +266,10 @@ over:
g.RunCh = g.DirCh g.RunCh = g.DirCh
ch = g.DirCh ch = g.DirCh
goto over continue // the C goto over: fight by running at it
} }
case 't': case 't':
if !g.getDir() { if !g.promptDirection() {
g.After = false g.After = false
} else { } else {
g.missile(g.Delta.Y, g.Delta.X) g.missile(g.Delta.Y, g.Delta.X)
@@ -282,7 +282,7 @@ over:
ch = g.LastComm ch = g.LastComm
g.Again = true g.Again = true
goto over continue // the C goto over: replay the last command
} }
case 'q': case 'q':
g.quaff() g.quaff()
@@ -334,7 +334,7 @@ over:
case 's': case 's':
g.search() g.search()
case 'z': case 'z':
if g.getDir() { if g.promptDirection() {
g.doZap() g.doZap()
} else { } else {
g.After = false g.After = false
@@ -360,7 +360,7 @@ over:
g.After = false // "legal" illegal command g.After = false // "legal" illegal command
case '^': case '^':
g.After = false g.After = false
if g.getDir() { if g.promptDirection() {
g.Delta.Y += p.Pos.Y g.Delta.Y += p.Pos.Y
g.Delta.X += p.Pos.X g.Delta.X += p.Pos.X
@@ -386,13 +386,13 @@ over:
g.Again = false g.Again = false
case 'm': case 'm':
g.MoveOn = true g.MoveOn = true
if !g.getDir() { if !g.promptDirection() {
g.After = false g.After = false
} else { } else {
ch = g.DirCh ch = g.DirCh
g.countCh = g.DirCh g.countCh = g.DirCh
goto over continue // the C goto over: move onto it without pickup
} }
case ')': case ')':
g.current(p.CurWeapon, "wielding", "") g.current(p.CurWeapon, "wielding", "")
@@ -416,6 +416,9 @@ over:
g.illcom(ch) g.illcom(ch)
} }
} }
return
}
} }
// wizardCommand handles the MASTER debug commands (command.c). // wizardCommand handles the MASTER debug commands (command.c).
@@ -450,7 +453,7 @@ func (g *RogueGame) wizardCommand(ch byte) {
case CTRL('X'): case CTRL('X'):
g.turnSee(p.On(SenseMonsters)) g.turnSee(p.On(SenseMonsters))
case CTRL('~'): case CTRL('~'):
if item := g.getItem("charge", KindWand); item != nil { if item, ok := g.promptPackItem("charge", KindWand); ok {
item.Charges = 10000 item.Charges = 10000
} }
case CTRL('I'): case CTRL('I'):
@@ -735,9 +738,9 @@ func (g *RogueGame) levitCheck() bool {
// call allows a user to call a potion, scroll, or ring something // call allows a user to call a potion, scroll, or ring something
// (command.c call). // (command.c call).
func (g *RogueGame) call() { func (g *RogueGame) call() {
obj := g.getItem("call", KindCallable) obj, ok := g.promptPackItem("call", KindCallable)
// Make certain that it is something that we want to name // Make certain that it is something that we want to name
if obj == nil { if !ok {
return return
} }
@@ -817,7 +820,7 @@ func (g *RogueGame) current(cur *Object, how, where string) {
} }
g.InvDescribe = false g.InvDescribe = false
g.addmsgf("%c) %s", cur.PackCh, g.invName(cur, true)) g.addmsgf("%c) %s", cur.PackCh, g.inventoryName(cur, true))
g.InvDescribe = true g.InvDescribe = true
if where != "" { if where != "" {

View File

@@ -50,6 +50,47 @@ func (p *Player) IsWearing(ring RingKind) bool {
return p.IsRing(Left, ring) || p.IsRing(Right, ring) return p.IsRing(Left, ring) || p.IsRing(Right, ring)
} }
// nextPackChar claims and returns the next unused pack character (pack.c
// pack_char).
func (p *Player) nextPackChar() byte {
for i := range p.PackUsed {
if !p.PackUsed[i] {
p.PackUsed[i] = true
return byte(i) + 'a'
}
}
return byte(len(p.PackUsed)) + 'a' // C would walk off the array here
}
// removeFromPack takes an item out of the pack: the whole entry, or one
// of a stack when all is false (the bookkeeping half of pack.c
// leave_pack). It returns the object that left the pack — a copy when
// newobj asks for a split.
func (p *Player) removeFromPack(obj *Object, newobj, all bool) *Object {
p.Inpack--
nobj := obj
if obj.Count > 1 && !all {
obj.Count--
if obj.Group != 0 {
p.Inpack++
}
if newobj {
copied := *obj
nobj = &copied
nobj.Count = 1
}
} else {
p.PackUsed[obj.PackCh-'a'] = false
detachObj(&p.Pack, obj)
}
return nobj
}
// attachMon pushes a monster onto the front of a list (list.c attach). // attachMon pushes a monster onto the front of a list (list.c attach).
func attachMon(list *[]*Monster, item *Monster) { func attachMon(list *[]*Monster, item *Monster) {
*list = append([]*Monster{item}, *list...) *list = append([]*Monster{item}, *list...)

View File

@@ -212,7 +212,7 @@ func (g *RogueGame) comeDown(int) {
// undo the things // undo the things
for _, tp := range g.Level.Objects { 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()) 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 { for _, tp := range g.Level.Monsters {
g.move(tp.Pos.Y, tp.Pos.X) 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) { if !tp.On(Invisible) || p.On(CanSeeInvisible) {
g.addch(tp.Disguise) g.addch(tp.Disguise)
} else { } else {
@@ -248,13 +248,13 @@ func (g *RogueGame) visuals(int) {
} }
// change the things // change the things
for _, tp := range g.Level.Objects { 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()) g.mvaddch(tp.Pos.Y, tp.Pos.X, g.rndThing())
} }
} }
// change the stairs // 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()) g.mvaddch(g.Level.Stairs.Y, g.Level.Stairs.X, g.rndThing())
} }

View File

@@ -10,7 +10,7 @@ func mkGameInput(t *testing.T) *RogueGame {
g := NewGame(Config{Seed: 5, Term: &testTerm{}}) g := NewGame(Config{Seed: 5, Term: &testTerm{}})
g.NewLevel() g.NewLevel()
g.Oldpos = g.Player.Pos g.Oldpos = g.Player.Pos
g.Oldrp = g.roomin(g.Player.Pos) g.Oldrp = g.roomIn(g.Player.Pos)
return g return g
} }

View File

@@ -47,7 +47,7 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
// place. // place.
g.Count = 0 g.Count = 0
g.Quiet = 0 g.Quiet = 0
g.runto(mp) g.runTo(mp)
// Let him know it was really a xeroc (if it was one). // Let him know it was really a xeroc (if it was one).
if tp.Type == 'X' && tp.Disguise != 'X' && !p.On(Blind) { if tp.Type == 'X' && tp.Disguise != 'X' && !p.On(Blind) {
tp.Disguise = 'X' tp.Disguise = 'X'
@@ -67,7 +67,7 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
didHit := false didHit := false
g.HasHit = g.Options.Terse && !g.ToDeath g.HasHit = g.Options.Terse && !g.ToDeath
if g.rollEm(&p.Creature, &tp.Creature, weap, thrown) { if g.rollAttacks(&p.Creature, &tp.Creature, weap, thrown) {
didHit = false didHit = false
if thrown { if thrown {
@@ -104,9 +104,10 @@ func (g *RogueGame) fight(mp Coord, weap *Object, thrown bool) bool {
return didHit return didHit
} }
// attack has the monster attack the player (fight.c attack). Returns -1 if // attack has the monster attack the player (fight.c attack). removed
// the monster removed itself from the level during its own attack. // reports that the monster took itself off the level during its own
func (g *RogueGame) attack(mp *Monster) int { // attack (the C -1 return).
func (g *RogueGame) attack(mp *Monster) (removed bool) {
p := &g.Player p := &g.Player
// Since this is an attack, stop running and any healing that was // Since this is an attack, stop running and any healing that was
// going on at the time. // going on at the time.
@@ -128,9 +129,8 @@ func (g *RogueGame) attack(mp *Monster) int {
mname := g.setMname(mp) mname := g.setMname(mp)
oldhp := p.Stats.HP oldhp := p.Stats.HP
removed := false
if g.rollEm(&mp.Creature, &p.Creature, nil, false) { if g.rollAttacks(&mp.Creature, &p.Creature, nil, false) {
if mp.Type != 'I' { if mp.Type != 'I' {
if g.HasHit { if g.HasHit {
g.addmsgf(". ") g.addmsgf(". ")
@@ -182,7 +182,7 @@ func (g *RogueGame) attack(mp *Monster) int {
// Rattlesnakes have poisonous bites // Rattlesnakes have poisonous bites
if !g.save(VsPoison) { if !g.save(VsPoison) {
if !p.IsWearing(RingSustainStrength) { if !p.IsWearing(RingSustainStrength) {
g.chgStr(-1) g.changeStrength(-1)
if !g.Options.Terse { if !g.Options.Terse {
g.msg("you feel a bite in your leg and now feel weaker") g.msg("you feel a bite in your leg and now feel weaker")
@@ -290,7 +290,7 @@ func (g *RogueGame) attack(mp *Monster) int {
removed = true removed = true
g.leavePack(steal, false, false) g.leavePack(steal, false, false)
g.msg("she stole %s!", g.invName(steal, true)) g.msg("she stole %s!", g.inventoryName(steal, true))
} }
} }
} }
@@ -317,11 +317,7 @@ func (g *RogueGame) attack(mp *Monster) int {
g.Count = 0 g.Count = 0
g.status() g.status()
if removed { return removed
return -1
}
return 0
} }
// swing returns true if the swing hits (fight.c swing). // swing returns true if the swing hits (fight.c swing).
@@ -332,8 +328,8 @@ func (g *RogueGame) swing(atLvl, opArm, wplus int) bool {
return res+wplus >= need return res+wplus >= need
} }
// rollEm rolls several attacks (fight.c roll_em). // rollAttacks rolls several attacks (fight.c roll_em).
func (g *RogueGame) rollEm(thatt, thdef *Creature, weap *Object, hurl bool) bool { func (g *RogueGame) rollAttacks(thatt, thdef *Creature, weap *Object, hurl bool) bool {
p := &g.Player p := &g.Player
att := &thatt.Stats att := &thatt.Stats
def := &thdef.Stats def := &thdef.Stats
@@ -553,7 +549,7 @@ func (g *RogueGame) removeMon(mp Coord, tp *Monster, waskill bool) {
g.Level.SetMonsterAt(mp.Y, mp.X, nil) g.Level.SetMonsterAt(mp.Y, mp.X, nil)
g.mvaddch(mp.Y, mp.X, tp.OldCh) g.mvaddch(mp.Y, mp.X, tp.OldCh)
detachMon(&g.Level.Monsters, tp) g.Level.RemoveMonster(tp)
if tp.On(Targeted) { if tp.On(Targeted) {
g.Kamikaze = false g.Kamikaze = false

View File

@@ -10,7 +10,7 @@ func mkGame(t *testing.T, seed int32) *RogueGame {
g := NewGame(Config{Seed: seed, Term: &testTerm{}}) g := NewGame(Config{Seed: seed, Term: &testTerm{}})
g.NewLevel() g.NewLevel()
g.Oldpos = g.Player.Pos g.Oldpos = g.Player.Pos
g.Oldrp = g.roomin(g.Player.Pos) g.Oldrp = g.roomIn(g.Player.Pos)
return g return g
} }
@@ -32,7 +32,7 @@ func TestRollEmParsesMultiAttackDice(t *testing.T) {
// With attacker level 20 vs armor 10, swing always hits // With attacker level 20 vs armor 10, swing always hits
// (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of // (rnd(20)+wplus >= (20-20)-10 is always true), so three attacks of
// 1x4 + str bonus 1 each must deal between 6 and 15 damage. // 1x4 + str bonus 1 each must deal between 6 and 15 damage.
if !g.rollEm(att, def, nil, false) { if !g.rollAttacks(att, def, nil, false) {
t.Fatal("attack with guaranteed swing missed") t.Fatal("attack with guaranteed swing missed")
} }

View File

@@ -110,7 +110,7 @@ type RogueGame struct {
// screen / messages // screen / messages
scr *Screen scr *Screen
Msgs MsgLine Msgs MessageLine
statusCache statusCache statusCache statusCache
invPage invPage // things.c discovery-list pagination statics invPage invPage // things.c discovery-list pagination statics
@@ -169,6 +169,7 @@ func NewGame(cfg Config) *RogueGame {
g.InvDescribe = true g.InvDescribe = true
g.Msgs.SaveMsg = true g.Msgs.SaveMsg = true
g.scr = NewScreen(cfg.Term) g.scr = NewScreen(cfg.Term)
g.Msgs.attach(g.scr, g.look, g.readchar)
g.FileName = cfg.Home + "/rogue.save" g.FileName = cfg.Home + "/rogue.save"
g.rogueOpts = cfg.RogueOpts g.rogueOpts = cfg.RogueOpts
@@ -242,7 +243,7 @@ func (g *RogueGame) playit() {
g.Oldpos = g.Player.Pos g.Oldpos = g.Player.Pos
g.Oldrp = g.roomin(g.Player.Pos) g.Oldrp = g.roomIn(g.Player.Pos)
for g.Playing { for g.Playing {
g.command() // command execution g.command() // command execution
} }

View File

@@ -10,9 +10,11 @@ import (
// maxMsg is io.c MAXMSG: how much message fits before --More--. // maxMsg is io.c MAXMSG: how much message fits before --More--.
const maxMsg = NumCols - len("--More--") - 1 const maxMsg = NumCols - len("--More--") - 1
// MsgLine is the io.c message machinery: the static msgbuf/newpos pair plus // MessageLine is the io.c message machinery: the static msgbuf/newpos
// the related globals (mpos, huh, and the message-behavior flags). // pair plus the related globals (mpos, huh, and the message-behavior
type MsgLine struct { // flags). It owns the top line of the screen; attach wires in the
// display and input it needs.
type MessageLine struct {
buf strings.Builder // msgbuf buf strings.Builder // msgbuf
newpos int newpos int
Mpos int // where cursor is on top line Mpos int // where cursor is on top line
@@ -20,49 +22,52 @@ type MsgLine struct {
SaveMsg bool // remember last msg SaveMsg bool // remember last msg
LowerMsg bool // messages should start w/lower case LowerMsg bool // messages should start w/lower case
MsgEsc bool // check for ESC from msg's --More-- MsgEsc bool // check for ESC from msg's --More--
scr *Screen // the top line lives on scr.Std
look func(wakeup bool) // redraw before a --More-- (misc.c look)
readChar func() byte // input for --More-- prompts
} }
// Msg displays a message at the top of the screen (io.c msg). It returns // Msg displays a message at the top of the screen (io.c msg). It returns
// Escape if the player escaped out of a --More--, ^Escape otherwise (the C // Escape if the player escaped out of a --More--, ^Escape otherwise (the
// convention: callers compare against ESCAPE). // C convention: callers compare against ESCAPE).
func (g *RogueGame) msg(format string, a ...any) int { func (m *MessageLine) Msg(format string, a ...any) int {
// if the string is "", just clear the line // if the string is "", just clear the line
if format == "" { if format == "" {
g.move(0, 0) m.scr.Std.Move(0, 0)
g.clrtoeol() m.scr.Std.Clrtoeol()
g.Msgs.Mpos = 0 m.Mpos = 0
return ^Escape return ^Escape
} }
// otherwise add to the message and flush it out // otherwise add to the message and flush it out
g.doaddf(format, a...) m.doaddf(format, a...)
return g.endmsg() return m.End()
} }
// addmsgf adds things to the current message (io.c addmsg). // Addf adds things to the current message (io.c addmsg).
func (g *RogueGame) addmsgf(format string, a ...any) { func (m *MessageLine) Addf(format string, a ...any) {
g.doaddf(format, a...) m.doaddf(format, a...)
} }
// endmsg displays a new msg, giving the player a chance to see the previous // End displays a new msg, giving the player a chance to see the previous
// one if it is up there with the --More-- (io.c endmsg). // one if it is up there with the --More-- (io.c endmsg).
func (g *RogueGame) endmsg() int { func (m *MessageLine) End() int {
m := &g.Msgs
if m.SaveMsg { if m.SaveMsg {
m.Huh = m.buf.String() m.Huh = m.buf.String()
} }
if m.Mpos != 0 { if m.Mpos != 0 {
g.look(false) m.look(false)
g.mvaddstr(0, m.Mpos, "--More--") m.scr.Std.MvAddStr(0, m.Mpos, "--More--")
g.refresh() m.scr.Refresh()
if !m.MsgEsc { if !m.MsgEsc {
g.waitFor(' ') m.waitForSpace()
} else { } else {
for { for {
ch := g.readchar() ch := m.readChar()
if ch == ' ' { if ch == ' ' {
break break
} }
@@ -85,30 +90,60 @@ func (g *RogueGame) endmsg() int {
out = string(toUpper(out[0])) + out[1:] out = string(toUpper(out[0])) + out[1:]
} }
g.mvaddstr(0, 0, out) m.scr.Std.MvAddStr(0, 0, out)
g.clrtoeol() m.scr.Std.Clrtoeol()
m.Mpos = m.newpos m.Mpos = m.newpos
m.newpos = 0 m.newpos = 0
m.buf.Reset() m.buf.Reset()
g.refresh() m.scr.Refresh()
return ^Escape return ^Escape
} }
// doaddf performs an add onto the message buffer (io.c doadd). // attach wires the message line to its display and input; NewGame and
func (g *RogueGame) doaddf(format string, a ...any) { // Restore call it once the screen and game exist.
m := &g.Msgs func (m *MessageLine) attach(scr *Screen, look func(bool), readChar func() byte) {
m.scr = scr
m.look = look
m.readChar = readChar
}
// waitForSpace absorbs input until the player types a space: the
// --More-- acknowledgement (io.c wait_for).
func (m *MessageLine) waitForSpace() {
for {
if m.readChar() == ' ' {
return
}
}
}
// doaddf performs an add onto the message buffer (io.c doadd).
func (m *MessageLine) doaddf(format string, a ...any) {
s := fmt.Sprintf(format, a...) s := fmt.Sprintf(format, a...)
if len(s)+m.newpos >= maxMsg { if len(s)+m.newpos >= maxMsg {
g.endmsg() m.End()
} }
m.buf.WriteString(s) m.buf.WriteString(s)
m.newpos = m.buf.Len() m.newpos = m.buf.Len()
} }
// msg, addmsgf, and endmsg are the game-side shorthands for the message
// line; the machinery lives on MessageLine.
func (g *RogueGame) msg(format string, a ...any) int {
return g.Msgs.Msg(format, a...)
}
func (g *RogueGame) addmsgf(format string, a ...any) {
g.Msgs.Addf(format, a...)
}
func (g *RogueGame) endmsg() {
g.Msgs.End()
}
// stepOk returns true if it is ok to step on ch (io.c step_ok). // stepOk returns true if it is ok to step on ch (io.c step_ok).
func stepOk(ch byte) bool { func stepOk(ch byte) bool {
switch ch { switch ch {

View File

@@ -51,6 +51,29 @@ func (l *Level) VisibleChar(y, x int) byte {
return l.Char(y, x) return l.Char(y, x)
} }
// ObjectAt finds the unclaimed object at (y, x) (misc.c find_obj).
func (l *Level) ObjectAt(y, x int) *Object {
for _, obj := range l.Objects {
if obj.Pos.Y == y && obj.Pos.X == x {
return obj
}
}
return nil
}
// AddObject puts an object on the level (list.c attach on lvl_obj).
func (l *Level) AddObject(obj *Object) { attachObj(&l.Objects, obj) }
// RemoveObject takes an object off the level (list.c detach on lvl_obj).
func (l *Level) RemoveObject(obj *Object) { detachObj(&l.Objects, obj) }
// AddMonster puts a monster on the level (list.c attach on mlist).
func (l *Level) AddMonster(m *Monster) { attachMon(&l.Monsters, m) }
// RemoveMonster takes a monster off the level (list.c detach on mlist).
func (l *Level) RemoveMonster(m *Monster) { detachMon(&l.Monsters, m) }
// goldCalc is the GOLDCALC macro: how much a gold pile is worth at depth. // goldCalc is the GOLDCALC macro: how much a gold pile is worth at depth.
func (g *RogueGame) goldCalc() int { func (g *RogueGame) goldCalc() int {
return g.rnd(50+10*g.Depth) + 2 return g.rnd(50+10*g.Depth) + 2

View File

@@ -223,21 +223,10 @@ func (g *RogueGame) showFloor() bool {
return true return true
} }
// findObj finds the unclaimed object at (y, x) (misc.c find_obj).
func (g *RogueGame) findObj(y, x int) *Object {
for _, obj := range g.Level.Objects {
if obj.Pos.Y == y && obj.Pos.X == x {
return obj
}
}
return nil
}
// eat lets her try to eat something (misc.c eat). // eat lets her try to eat something (misc.c eat).
func (g *RogueGame) eat() { func (g *RogueGame) eat() {
obj := g.getItem("eat", KindFood) obj, ok := g.promptPackItem("eat", KindFood)
if obj == nil { if !ok {
return return
} }
@@ -305,9 +294,9 @@ func (g *RogueGame) checkLevel() {
} }
} }
// chgStr modifies the player's strength, keeping track of the highest it // changeStrength modifies the player's strength, keeping track of the
// has been (misc.c chg_str). // highest it has been (misc.c chg_str).
func (g *RogueGame) chgStr(amt int) { func (g *RogueGame) changeStrength(amt int) {
if amt == 0 { if amt == 0 {
return return
} }
@@ -362,10 +351,10 @@ func (g *RogueGame) addHaste(potion bool) bool {
// aggravate aggravates all the monsters on this level (misc.c aggravate). // aggravate aggravates all the monsters on this level (misc.c aggravate).
func (g *RogueGame) 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...) monsters := append([]*Monster(nil), g.Level.Monsters...)
for _, mp := range monsters { for _, mp := range monsters {
g.runto(mp.Pos) g.runTo(mp.Pos)
} }
} }
@@ -406,9 +395,9 @@ func (g *RogueGame) isCurrent(obj *Object) bool {
return false return false
} }
// getDir sets up the direction coordinate for use in various "prefix" // promptDirection sets up the direction coordinate for use in various
// commands (misc.c get_dir). // "prefix" commands (misc.c get_dir).
func (g *RogueGame) getDir() bool { func (g *RogueGame) promptDirection() bool {
if g.Again && g.LastDir != 0 { if g.Again && g.LastDir != 0 {
g.Delta = g.lastDelt g.Delta = g.lastDelt
g.DirCh = g.LastDir g.DirCh = g.LastDir

View File

@@ -31,13 +31,13 @@ func (g *RogueGame) randMonster(wander bool) byte {
func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) { func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) {
levAdd := max(g.Depth-AmuletLevel, 0) levAdd := max(g.Depth-AmuletLevel, 0)
attachMon(&g.Level.Monsters, tp) g.Level.AddMonster(tp)
tp.Type = typ tp.Type = typ
tp.Disguise = typ tp.Disguise = typ
tp.Pos = cp tp.Pos = cp
g.move(cp.Y, cp.X) g.move(cp.Y, cp.X)
tp.OldCh = g.inch() tp.OldCh = g.inch()
tp.Room = g.roomin(cp) tp.Room = g.roomIn(cp)
g.Level.SetMonsterAt(cp.Y, cp.X, tp) g.Level.SetMonsterAt(cp.Y, cp.X, tp)
mp := &g.Monsters[tp.Type-'A'] mp := &g.Monsters[tp.Type-'A']
tp.Stats.Lvl = mp.Stats.Lvl + levAdd tp.Stats.Lvl = mp.Stats.Lvl + levAdd
@@ -57,7 +57,7 @@ func (g *RogueGame) newMonster(tp *Monster, typ byte, cp Coord) {
tp.Pack = nil tp.Pack = nil
if g.Player.IsWearing(RingAggravateMonsters) { if g.Player.IsWearing(RingAggravateMonsters) {
g.runto(cp) g.runTo(cp)
} }
if typ == 'X' { if typ == 'X' {
@@ -92,7 +92,7 @@ func (g *RogueGame) wanderer() {
var cp Coord var cp Coord
for { for {
cp, _ = g.findFloor(true) cp, _ = g.findFloor(true)
if g.roomin(cp) != g.Player.Room { if g.roomIn(cp) != g.Player.Room {
break break
} }
} }
@@ -111,7 +111,7 @@ func (g *RogueGame) wanderer() {
g.standend() g.standend()
} }
g.runto(tp.Pos) g.runTo(tp.Pos)
} }
// wakeMonster is what to do when the hero steps next to a monster // wakeMonster is what to do when the hero steps next to a monster

View File

@@ -2,16 +2,17 @@ package game
// move.c — hero movement commands. // move.c — hero movement commands.
// doRun starts the hero running (move.c do_run). // startRun starts the hero running (move.c do_run).
func (g *RogueGame) doRun(ch byte) { func (g *RogueGame) startRun(ch byte) {
g.Running = true g.Running = true
g.After = false g.After = false
g.RunCh = ch g.RunCh = ch
} }
// doMove checks that a move is legal and handles the consequences — // moveHero checks that a move is legal and handles the consequences —
// fighting, picking up, etc. (move.c do_move). // fighting, picking up, etc. (move.c do_move). The C `goto over`
func (g *RogueGame) doMove(dy, dx int) { // re-check after a passage turn is the retry loop.
func (g *RogueGame) moveHero(dy, dx int) {
p := &g.Player p := &g.Player
g.Firstmove = false g.Firstmove = false
@@ -24,7 +25,7 @@ func (g *RogueGame) doMove(dy, dx int) {
// Do a confused move (maybe) // Do a confused move (maybe)
var nh Coord var nh Coord
if p.On(Confused) && g.rnd(5) != 0 { if p.On(Confused) && g.rnd(5) != 0 {
nh = g.rndmove(&p.Creature) nh = g.randomStep(&p.Creature)
if nh == p.Pos { if nh == p.Pos {
g.After = false g.After = false
g.Running = false g.Running = false
@@ -36,9 +37,9 @@ func (g *RogueGame) doMove(dy, dx int) {
nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx} nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx}
} }
over: for {
// Check if he tried to move off the screen or make an illegal diagonal // Check if he tried to move off the screen or make an illegal
// move, and stop him if he did. // diagonal move, and stop him if he did.
hitBound := nh.X < 0 || nh.X >= NumCols || nh.Y <= 0 || nh.Y >= NumLines-1 hitBound := nh.X < 0 || nh.X >= NumCols || nh.Y <= 0 || nh.Y >= NumLines-1
var ( var (
@@ -81,8 +82,76 @@ over:
switch ch { switch ch {
case ' ', '|', '-': case ' ', '|', '-':
if g.Options.PassGo && g.Running && p.Room.Flags.Has(Gone) && if turn, ndy, ndx := g.passageTurn(dy, dx); turn {
!p.On(Blind) { dy, dx = ndy, ndx
g.turnRefresh()
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)
}
}
return
}
}
// 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 var b1, b2 bool
switch g.RunCh { switch g.RunCh {
@@ -99,13 +168,7 @@ over:
dy = 1 dy = 1
} }
dx = 0 return true, dy, 0
g.turnref()
nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx}
goto over
} }
case 'j', 'k': case 'j', 'k':
b1 = p.Pos.X != 0 && g.turnOk(p.Pos.Y, p.Pos.X-1) b1 = p.Pos.X != 0 && g.turnOk(p.Pos.Y, p.Pos.X-1)
@@ -120,66 +183,15 @@ over:
dx = 1 dx = 1
} }
dy = 0 return true, 0, dx
g.turnref()
nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx}
goto over
}
} }
} }
g.Running = false return false, dy, dx
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) // finishMove is the move_stuff label in do_move: complete the step.
case Trap: func (g *RogueGame) finishMove(nh Coord, fl PlaceFlags) {
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)
}
}
}
// moveStuff is the move_stuff label in do_move: complete the step.
func (g *RogueGame) moveStuff(nh Coord, fl PlaceFlags) {
p := &g.Player p := &g.Player
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt()) 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) return pp.Ch == Door || pp.Flags&(FReal|FPassage) == (FReal|FPassage)
} }
// turnref decides whether to refresh at a passage turning (move.c turnref). // turnRefresh decides whether to refresh at a passage turning (move.c
func (g *RogueGame) turnref() { // turnref).
func (g *RogueGame) turnRefresh() {
p := &g.Player p := &g.Player
pp := g.Level.At(p.Pos.Y, p.Pos.X) 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). // springTrap makes him pay for stepping on a trap (move.c be_trapped).
func (g *RogueGame) beTrapped(tc Coord) TrapKind { func (g *RogueGame) springTrap(tc Coord) TrapKind {
p := &g.Player p := &g.Player
if p.On(Levitating) { if p.On(Levitating) {
return TrapRust // anything that's not a door or teleport 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) { if !p.IsWearing(RingSustainStrength) && !g.save(VsPoison) {
g.chgStr(-1) g.changeStrength(-1)
} }
g.msg("a small dart just hit you in the shoulder") g.msg("a small dart just hit you in the shoulder")
@@ -328,9 +341,9 @@ func (g *RogueGame) beTrapped(tc Coord) TrapKind {
return tr return tr
} }
// rndmove moves in a random direction if the monster/person is confused // randomStep moves in a random direction if the monster/person is
// (move.c rndmove). // confused (move.c rndmove).
func (g *RogueGame) rndmove(who *Creature) Coord { func (g *RogueGame) randomStep(who *Creature) Coord {
ret := Coord{ ret := Coord{
Y: who.Pos.Y + g.rnd(3) - 1, Y: who.Pos.Y + g.rnd(3) - 1,
X: who.Pos.X + g.rnd(3) - 1, X: who.Pos.X + g.rnd(3) - 1,

View File

@@ -27,8 +27,8 @@ func (g *RogueGame) NewLevel() {
// go with them (the garbage collector is our free_list). // go with them (the garbage collector is our free_list).
g.Level.Monsters = nil g.Level.Monsters = nil
g.Level.Objects = nil g.Level.Objects = nil
g.doRooms() // Draw rooms g.digRooms() // Draw rooms
g.doPassages() // Draw passages g.digPassages() // Draw passages
p.NoFood++ p.NoFood++
@@ -61,7 +61,7 @@ func (g *RogueGame) NewLevel() {
g.SeenStairs = false g.SeenStairs = false
for _, tp := range g.Level.Monsters { for _, tp := range g.Level.Monsters {
tp.Room = g.roomin(tp.Pos) tp.Room = g.roomIn(tp.Pos)
} }
hero, _ := g.findFloor(true) 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). // randomRoom picks a room that is really there (new_level.c rnd_room).
func (g *RogueGame) rndRoom() int { func (g *RogueGame) randomRoom() int {
for { for {
rm := g.rnd(MaxRooms) rm := g.rnd(MaxRooms)
if !g.Level.Rooms[rm].Flags.Has(Gone) { if !g.Level.Rooms[rm].Flags.Has(Gone) {
@@ -98,14 +98,14 @@ func (g *RogueGame) putThings() {
} }
// check for treasure rooms, and if so, put it in. // check for treasure rooms, and if so, put it in.
if g.rnd(treasRoomChance) == 0 { if g.rnd(treasRoomChance) == 0 {
g.treasRoom() g.treasureRoom()
} }
// Do MAXOBJ attempts to put things on a level // Do MAXOBJ attempts to put things on a level
for range MaxObj { for range MaxObj {
if g.rnd(100) < 36 { if g.rnd(100) < 36 {
// Pick a new object and link it in the list // Pick a new object and link it in the list
obj := g.newThing() obj := g.newThing()
attachObj(&g.Level.Objects, obj) g.Level.AddObject(obj)
// Put it somewhere // Put it somewhere
obj.Pos, _ = g.findFloor(false) obj.Pos, _ = g.findFloor(false)
g.Level.SetChar(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph()) g.Level.SetChar(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph())
@@ -115,7 +115,7 @@ func (g *RogueGame) putThings() {
// yet, put it somewhere on the ground // yet, put it somewhere on the ground
if g.Depth >= AmuletLevel && !g.HasAmulet { if g.Depth >= AmuletLevel && !g.HasAmulet {
obj := newObject() obj := newObject()
attachObj(&g.Level.Objects, obj) g.Level.AddObject(obj)
obj.Damage = dice("0x0") obj.Damage = dice("0x0")
obj.HurlDmg = dice("0x0") obj.HurlDmg = dice("0x0")
obj.ArmorClass = 11 obj.ArmorClass = 11
@@ -126,9 +126,9 @@ func (g *RogueGame) putThings() {
} }
} }
// treasRoom adds a treasure room (new_level.c treas_room). // treasureRoom adds a treasure room (new_level.c treas_room).
func (g *RogueGame) treasRoom() { func (g *RogueGame) treasureRoom() {
rp := &g.Level.Rooms[g.rndRoom()] rp := &g.Level.Rooms[g.randomRoom()]
spots := min((rp.Max.Y-2)*(rp.Max.X-2)-minTreas, maxTreas-minTreas) spots := min((rp.Max.Y-2)*(rp.Max.X-2)-minTreas, maxTreas-minTreas)
@@ -137,7 +137,7 @@ func (g *RogueGame) treasRoom() {
mp, _ := g.findFloorIn(rp, 2*maxTries, false) mp, _ := g.findFloorIn(rp, 2*maxTries, false)
tp := g.newThing() tp := g.newThing()
tp.Pos = mp tp.Pos = mp
attachObj(&g.Level.Objects, tp) g.Level.AddObject(tp)
g.Level.SetChar(mp.Y, mp.X, tp.Kind.Glyph()) g.Level.SetChar(mp.Y, mp.X, tp.Kind.Glyph())
} }

View File

@@ -9,7 +9,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
fromFloor := false fromFloor := false
if obj == nil { if obj == nil {
if obj = g.findObj(p.Pos.Y, p.Pos.X); obj == nil { if obj = g.Level.ObjectAt(p.Pos.Y, p.Pos.X); obj == nil {
return return
} }
@@ -18,7 +18,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
// Check for and deal with scare monster scrolls // Check for and deal with scare monster scrolls
if obj.Kind == KindScroll && obj.ScrollKind() == ScrollScareMonster && obj.Flags.Has(WasFound) { if obj.Kind == KindScroll && obj.ScrollKind() == ScrollScareMonster && obj.Flags.Has(WasFound) {
detachObj(&g.Level.Objects, obj) g.Level.RemoveObject(obj)
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh()) g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh())
if p.Room.Flags.Has(Gone) { if p.Room.Flags.Has(Gone) {
@@ -34,7 +34,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
if len(p.Pack) == 0 { if len(p.Pack) == 0 {
p.Pack = append(p.Pack, obj) p.Pack = append(p.Pack, obj)
obj.PackCh = g.packChar() obj.PackCh = p.nextPackChar()
p.Inpack++ p.Inpack++
} else { } else {
// Walk the pack looking for the insertion point, keeping items of // Walk the pack looking for the insertion point, keeping items of
@@ -112,7 +112,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
return return
} }
obj.PackCh = g.packChar() obj.PackCh = p.nextPackChar()
p.Pack = append(p.Pack[:lp+1], p.Pack = append(p.Pack[:lp+1],
append([]*Object{obj}, p.Pack[lp+1:]...)...) append([]*Object{obj}, p.Pack[lp+1:]...)...)
} }
@@ -137,7 +137,7 @@ func (g *RogueGame) addPack(obj *Object, silent bool) {
g.addmsgf("you now have ") g.addmsgf("you now have ")
} }
g.msg("%s (%c)", g.invName(obj, !g.Options.Terse), obj.PackCh) g.msg("%s (%c)", g.inventoryName(obj, !g.Options.Terse), obj.PackCh)
} }
} }
@@ -168,7 +168,7 @@ func (g *RogueGame) packRoom(fromFloor bool, obj *Object) bool {
} }
if fromFloor { if fromFloor {
detachObj(&g.Level.Objects, obj) g.Level.RemoveObject(obj)
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh()) g.mvaddch(p.Pos.Y, p.Pos.X, g.floorCh())
if p.Room.Flags.Has(Gone) { if p.Room.Flags.Has(Gone) {
@@ -181,46 +181,17 @@ func (g *RogueGame) packRoom(fromFloor bool, obj *Object) bool {
return true return true
} }
// leavePack takes an item out of the pack (pack.c leave_pack). // leavePack takes an item out of the pack (pack.c leave_pack), keeping
// the repeat-command bookkeeping; the pack surgery is
// Player.removeFromPack.
func (g *RogueGame) leavePack(obj *Object, newobj, all bool) *Object { func (g *RogueGame) leavePack(obj *Object, newobj, all bool) *Object {
p := &g.Player
p.Inpack--
nobj := obj
if obj.Count > 1 && !all { if obj.Count > 1 && !all {
g.LastPick = obj g.LastPick = obj
obj.Count--
if obj.Group != 0 {
p.Inpack++
}
if newobj {
copied := *obj
nobj = &copied
nobj.Count = 1
}
} else { } else {
g.LastPick = nil g.LastPick = nil
p.PackUsed[obj.PackCh-'a'] = false
detachObj(&p.Pack, obj)
} }
return nobj return g.Player.removeFromPack(obj, newobj, all)
}
// packChar returns the next unused pack character (pack.c pack_char).
func (g *RogueGame) packChar() byte {
p := &g.Player
for i := range p.PackUsed {
if !p.PackUsed[i] {
p.PackUsed[i] = true
return byte(i) + 'a'
}
}
return byte(len(p.PackUsed)) + 'a' // C would walk off the array here
} }
// inventory lists what is in the pack; returns true if there is something // inventory lists what is in the pack; returns true if there is something
@@ -236,7 +207,7 @@ func (g *RogueGame) inventory(list []*Object, kind ObjectKind) bool {
g.NObjs++ g.NObjs++
g.Msgs.MsgEsc = true g.Msgs.MsgEsc = true
line := string(item.PackCh) + ") " + g.invName(item, false) line := string(item.PackCh) + ") " + g.inventoryName(item, false)
if g.addLine("%s", line) == Escape { if g.addLine("%s", line) == Escape {
g.Msgs.MsgEsc = false g.Msgs.MsgEsc = false
g.msg("") g.msg("")
@@ -277,7 +248,7 @@ func (g *RogueGame) pickUp(ch byte) {
return return
} }
obj := g.findObj(p.Pos.Y, p.Pos.X) obj := g.Level.ObjectAt(p.Pos.Y, p.Pos.X)
if g.MoveOn { if g.MoveOn {
g.moveMsg(obj) g.moveMsg(obj)
@@ -291,7 +262,7 @@ func (g *RogueGame) pickUp(ch byte) {
} }
g.money(obj.GoldValue) g.money(obj.GoldValue)
detachObj(&g.Level.Objects, obj) g.Level.RemoveObject(obj)
p.Room.GoldVal = 0 p.Room.GoldVal = 0
default: default:
@@ -323,7 +294,7 @@ func (g *RogueGame) moveMsg(obj *Object) {
g.addmsgf("you ") g.addmsgf("you ")
} }
g.msg("moved onto %s", g.invName(obj, true)) g.msg("moved onto %s", g.inventoryName(obj, true))
} }
// pickyInven allows the player to inventory a single item (pack.c // pickyInven allows the player to inventory a single item (pack.c
@@ -337,7 +308,7 @@ func (g *RogueGame) pickyInven() {
} }
if len(p.Pack) == 1 { if len(p.Pack) == 1 {
g.msg("a) %s", g.invName(p.Pack[0], false)) g.msg("a) %s", g.inventoryName(p.Pack[0], false))
return return
} }
@@ -354,7 +325,7 @@ func (g *RogueGame) pickyInven() {
for _, obj := range p.Pack { for _, obj := range p.Pack {
if mch == obj.PackCh { if mch == obj.PackCh {
g.msg("%c) %s", mch, g.invName(obj, false)) g.msg("%c) %s", mch, g.inventoryName(obj, false))
return return
} }
@@ -363,23 +334,24 @@ func (g *RogueGame) pickyInven() {
g.msg("'%s' not in pack", unctrl(mch)) g.msg("'%s' not in pack", unctrl(mch))
} }
// getItem picks something out of a pack for a purpose (pack.c get_item). // promptPackItem picks something out of a pack for a purpose (pack.c
func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object { // get_item); ok reports whether the player chose an item.
func (g *RogueGame) promptPackItem(purpose string, kind ObjectKind) (obj *Object, ok bool) {
p := &g.Player p := &g.Player
if len(p.Pack) == 0 { if len(p.Pack) == 0 {
g.msg("you aren't carrying anything") g.msg("you aren't carrying anything")
return nil return nil, false
} }
if g.Again { if g.Again {
if g.LastPick != nil { if g.LastPick != nil {
return g.LastPick return g.LastPick, true
} }
g.msg("you ran out") g.msg("you ran out")
return nil return nil, false
} }
for { for {
@@ -402,7 +374,7 @@ func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
g.After = false g.After = false
g.msg("") g.msg("")
return nil return nil, false
} }
g.NObjs = 1 // normal case: person types one char g.NObjs = 1 // normal case: person types one char
@@ -411,7 +383,7 @@ func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
if !g.inventory(p.Pack, kind) { if !g.inventory(p.Pack, kind) {
g.After = false g.After = false
return nil return nil, false
} }
continue continue
@@ -419,7 +391,7 @@ func (g *RogueGame) getItem(purpose string, kind ObjectKind) *Object {
for _, obj := range p.Pack { for _, obj := range p.Pack {
if obj.PackCh == ch { if obj.PackCh == ch {
return obj return obj, true
} }
} }

View File

@@ -2,8 +2,8 @@ package game
// passages.c — draw the connecting passages. // passages.c — draw the connecting passages.
// doPassages draws all the passages on a level (passages.c do_passages). // digPassages draws all the passages on a level (passages.c do_passages).
func (g *RogueGame) doPassages() { func (g *RogueGame) digPassages() {
var ( var (
isconn [MaxRooms][MaxRooms]bool isconn [MaxRooms][MaxRooms]bool
ingraph [MaxRooms]bool ingraph [MaxRooms]bool
@@ -41,7 +41,7 @@ func (g *RogueGame) doPassages() {
// otherwise, connect new room to the graph, and draw a tunnel // otherwise, connect new room to the graph, and draw a tunnel
// to it // to it
ingraph[r2] = true //nolint:gosec // G602: rnd(MaxRooms) bounded ingraph[r2] = true //nolint:gosec // G602: rnd(MaxRooms) bounded
g.conn(r1, r2) g.connectRooms(r1, r2)
isconn[r1][r2] = true isconn[r1][r2] = true
isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded
roomcount++ roomcount++
@@ -69,18 +69,18 @@ func (g *RogueGame) doPassages() {
} }
// if there is one, connect it and look for the next added passage // if there is one, connect it and look for the next added passage
if j != 0 { if j != 0 {
g.conn(r1, r2) g.connectRooms(r1, r2)
isconn[r1][r2] = true isconn[r1][r2] = true
isconn[r2][r1] = true //nolint:gosec // G602: rnd(MaxRooms) bounded 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 // connectRooms draws a corridor from a room in a certain direction
// conn). // (passages.c conn).
func (g *RogueGame) conn(r1, r2 int) { func (g *RogueGame) connectRooms(r1, r2 int) {
var ( var (
rm int rm int
direc byte direc byte
@@ -193,13 +193,13 @@ func (g *RogueGame) conn(r1, r2 int) {
if !rpf.Flags.Has(Gone) { if !rpf.Flags.Has(Gone) {
g.door(rpf, spos) g.door(rpf, spos)
} else { } else {
g.putpass(spos) g.putPassage(spos)
} }
if !rpt.Flags.Has(Gone) { if !rpt.Flags.Has(Gone) {
g.door(rpt, epos) g.door(rpt, epos)
} else { } else {
g.putpass(epos) g.putPassage(epos)
} }
// Get ready to move... // Get ready to move...
curr := spos 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 // Check if we are at the turn place, if so do the turn
if distance == turnSpot { if distance == turnSpot {
for ; turnDistance > 0; turnDistance-- { for ; turnDistance > 0; turnDistance-- {
g.putpass(curr) g.putPassage(curr)
curr.X += turnDelta.X curr.X += turnDelta.X
curr.Y += turnDelta.Y curr.Y += turnDelta.Y
} }
} }
// Continue digging along // Continue digging along
g.putpass(curr) g.putPassage(curr)
distance-- 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). // putpass).
func (g *RogueGame) putpass(cp Coord) { func (g *RogueGame) putPassage(cp Coord) {
pp := g.Level.At(cp.Y, cp.X) pp := g.Level.At(cp.Y, cp.X)
pp.Flags.Set(FPassage) pp.Flags.Set(FPassage)
@@ -302,8 +302,8 @@ func (g *RogueGame) addPass() {
} }
} }
// passnum assigns a number to each passageway (passages.c passnum). // numberPassages assigns a number to each passageway (passages.c passnum).
func (g *RogueGame) passnum() { func (g *RogueGame) numberPassages() {
g.pnum = 0 g.pnum = 0
g.newpnum = false g.newpnum = false
@@ -315,14 +315,14 @@ func (g *RogueGame) passnum() {
rp := &g.Level.Rooms[i] rp := &g.Level.Rooms[i]
for j := range rp.Exits { for j := range rp.Exits {
g.newpnum = true 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). // numpass).
func (g *RogueGame) numpass(y, x int) { func (g *RogueGame) numberPassage(y, x int) {
if x >= NumCols || x < 0 || y >= NumLines || y <= 0 { if x >= NumCols || x < 0 || y >= NumLines || y <= 0 {
return return
} }
@@ -348,10 +348,10 @@ func (g *RogueGame) numpass(y, x int) {
*fp |= PlaceFlags(g.pnum) //nolint:gosec // G115: pnum < MaxPass=13 *fp |= PlaceFlags(g.pnum) //nolint:gosec // G115: pnum < MaxPass=13
// recurse on the surrounding places // recurse on the surrounding places
g.numpass(y+1, x) g.numberPassage(y+1, x)
g.numpass(y-1, x) g.numberPassage(y-1, x)
g.numpass(y, x+1) g.numberPassage(y, x+1)
g.numpass(y, x-1) g.numberPassage(y, x-1)
} }
// abs is C abs() for ints. // abs is C abs() for ints.

View File

@@ -16,9 +16,9 @@ type pact struct {
// quaff drinks a potion from the pack (potions.c quaff). // quaff drinks a potion from the pack (potions.c quaff).
func (g *RogueGame) quaff() { func (g *RogueGame) quaff() {
p := &g.Player p := &g.Player
obj := g.getItem("quaff", KindPotion) obj, ok := g.promptPackItem("quaff", KindPotion)
// Make certain that it is something that we want to drink // Make certain that it is something that we want to drink
if obj == nil { if !ok {
return return
} }
@@ -43,13 +43,13 @@ func (g *RogueGame) quaff() {
switch obj.PotionKind() { switch obj.PotionKind() {
case PotionConfusion: case PotionConfusion:
g.doPot(PotionConfusion, !trip) g.applyPotionFuse(PotionConfusion, !trip)
case PotionPoison: case PotionPoison:
g.Items.Potions[PotionPoison].Know = true g.Items.Potions[PotionPoison].Know = true
if p.IsWearing(RingSustainStrength) { if p.IsWearing(RingSustainStrength) {
g.msg("you feel momentarily sick") g.msg("you feel momentarily sick")
} else { } else {
g.chgStr(-(g.rnd(3) + 1)) g.changeStrength(-(g.rnd(3) + 1))
g.msg("you feel very sick now") g.msg("you feel very sick now")
g.comeDown(0) g.comeDown(0)
} }
@@ -64,7 +64,7 @@ func (g *RogueGame) quaff() {
g.msg("you begin to feel better") g.msg("you begin to feel better")
case PotionGainStrength: case PotionGainStrength:
g.Items.Potions[PotionGainStrength].Know = true g.Items.Potions[PotionGainStrength].Know = true
g.chgStr(1) g.changeStrength(1)
g.msg("you feel stronger, now. What bulging muscles!") g.msg("you feel stronger, now. What bulging muscles!")
case PotionDetectMonsters: case PotionDetectMonsters:
p.Flags.Set(SenseMonsters) p.Flags.Set(SenseMonsters)
@@ -118,11 +118,11 @@ func (g *RogueGame) quaff() {
g.SeenStairs = g.seenStairs() g.SeenStairs = g.seenStairs()
} }
g.doPot(PotionLSD, true) g.applyPotionFuse(PotionLSD, true)
case PotionSeeInvisible: case PotionSeeInvisible:
show := p.On(CanSeeInvisible) show := p.On(CanSeeInvisible)
g.doPot(PotionSeeInvisible, false) g.applyPotionFuse(PotionSeeInvisible, false)
if !show { if !show {
g.invisOn() g.invisOn()
@@ -177,9 +177,9 @@ func (g *RogueGame) quaff() {
g.msg("hey, this tastes great. It make you feel warm all over") g.msg("hey, this tastes great. It make you feel warm all over")
case PotionBlindness: case PotionBlindness:
g.doPot(PotionBlindness, true) g.applyPotionFuse(PotionBlindness, true)
case PotionLevitation: case PotionLevitation:
g.doPot(PotionLevitation, true) g.applyPotionFuse(PotionLevitation, true)
} }
g.status() g.status()
@@ -194,9 +194,9 @@ func (g *RogueGame) raiseLevel() {
g.checkLevel() g.checkLevel()
} }
// doPot does a potion with standard setup: it uses a fuse and turns on a // applyPotionFuse does a potion with standard setup: it uses a fuse and
// flag (potions.c do_pot). // turns on a flag (potions.c do_pot).
func (g *RogueGame) doPot(kind PotionKind, knowit bool) { func (g *RogueGame) applyPotionFuse(kind PotionKind, knowit bool) {
pp := &g.data.pActions[kind] pp := &g.data.pActions[kind]
if !g.Items.Potions[kind].Know { if !g.Items.Potions[kind].Know {
g.Items.Potions[kind].Know = knowit g.Items.Potions[kind].Know = knowit

View File

@@ -7,9 +7,9 @@ import "fmt"
// ringOn puts a ring on a hand (rings.c ring_on). // ringOn puts a ring on a hand (rings.c ring_on).
func (g *RogueGame) ringOn() { func (g *RogueGame) ringOn() {
p := &g.Player p := &g.Player
obj := g.getItem("put on", KindRing) obj, ok := g.promptPackItem("put on", KindRing)
// Make certain that it is something that we want to wear // Make certain that it is something that we want to wear
if obj == nil { if !ok {
return return
} }
@@ -54,7 +54,7 @@ func (g *RogueGame) ringOn() {
// Calculate the effect it has on the poor guy. // Calculate the effect it has on the poor guy.
switch obj.RingKind() { switch obj.RingKind() {
case RingAddStrength: case RingAddStrength:
g.chgStr(obj.Bonus) g.changeStrength(obj.Bonus)
case RingSeeInvisible: case RingSeeInvisible:
g.invisOn() g.invisOn()
case RingAggravateMonsters: case RingAggravateMonsters:
@@ -65,7 +65,7 @@ func (g *RogueGame) ringOn() {
g.addmsgf("you are now wearing ") g.addmsgf("you are now wearing ")
} }
g.msg("%s (%c)", g.invName(obj, true), obj.PackCh) g.msg("%s (%c)", g.inventoryName(obj, true), obj.PackCh)
} }
// ringOff takes off a ring (rings.c ring_off). // ringOff takes off a ring (rings.c ring_off).
@@ -103,7 +103,7 @@ func (g *RogueGame) ringOff() {
} }
if g.dropCheck(obj) { if g.dropCheck(obj) {
g.msg("was wearing %s(%c)", g.invName(obj, true), obj.PackCh) g.msg("was wearing %s(%c)", g.inventoryName(obj, true), obj.PackCh)
} }
} }

View File

@@ -189,7 +189,7 @@ func (g *RogueGame) totalWinner() {
} }
g.scr.Std.MvPrintwf(line, 0, "%c) %5d %s", obj.PackCh, worth, g.scr.Std.MvPrintwf(line, 0, "%c) %5d %s", obj.PackCh, worth,
g.invName(obj, false)) g.inventoryName(obj, false))
line++ line++
p.Purse += worth p.Purse += worth
} }

View File

@@ -17,9 +17,9 @@ type mazeState struct {
const goldGrp = 1 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). // do_rooms).
func (g *RogueGame) doRooms() { func (g *RogueGame) digRooms() {
var bsze Coord // maximum room size var bsze Coord // maximum room size
bsze.X = NumCols / 3 bsze.X = NumCols / 3
@@ -34,7 +34,7 @@ func (g *RogueGame) doRooms() {
// Put the gone rooms, if any, on the level // Put the gone rooms, if any, on the level
leftOut := g.rnd(4) leftOut := g.rnd(4)
for range leftOut { 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 // dig and populate all the rooms on the level
for i := range g.Level.Rooms { for i := range g.Level.Rooms {
@@ -105,7 +105,7 @@ func (g *RogueGame) doRooms() {
gold.Flags = Stackable gold.Flags = Stackable
gold.Group = goldGrp gold.Group = goldGrp
gold.Kind = KindGold gold.Kind = KindGold
attachObj(&g.Level.Objects, gold) g.Level.AddObject(gold)
} }
// Put the monster in // Put the monster in
prob := 25 prob := 25
@@ -126,7 +126,7 @@ func (g *RogueGame) doRooms() {
// rooms; for maze rooms, draws the maze (rooms.c draw_room). // rooms; for maze rooms, draws the maze (rooms.c draw_room).
func (g *RogueGame) drawRoom(rp *Room) { func (g *RogueGame) drawRoom(rp *Room) {
if rp.Flags.Has(Maze) { if rp.Flags.Has(Maze) {
g.doMaze(rp) g.digMaze(rp)
return return
} }
@@ -158,8 +158,8 @@ func (g *RogueGame) horiz(rp *Room, starty int) {
} }
} }
// doMaze digs a maze (rooms.c do_maze). // digMaze digs a maze (rooms.c do_maze).
func (g *RogueGame) doMaze(rp *Room) { func (g *RogueGame) digMaze(rp *Room) {
m := &g.maze m := &g.maze
for y := range m.maze { for y := range m.maze {
for x := range m.maze[y] { for x := range m.maze[y] {
@@ -175,7 +175,7 @@ func (g *RogueGame) doMaze(rp *Room) {
starty := (g.rnd(rp.Max.Y) / 2) * 2 starty := (g.rnd(rp.Max.Y) / 2) * 2
startx := (g.rnd(rp.Max.X) / 2) * 2 startx := (g.rnd(rp.Max.X) / 2) * 2
pos := Coord{Y: starty + m.starty, X: startx + m.startx} pos := Coord{Y: starty + m.starty, X: startx + m.startx}
g.putpass(pos) g.putPassage(pos)
g.dig(starty, startx) g.dig(starty, startx)
} }
@@ -211,8 +211,8 @@ func (g *RogueGame) dig(y, x int) {
return return
} }
g.accntMaze(y, x, nexty, nextx) g.accountMaze(y, x, nexty, nextx)
g.accntMaze(nexty, nextx, y, x) g.accountMaze(nexty, nextx, y, x)
var pos Coord var pos Coord
if nexty == y { 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.Y = nexty + m.starty
pos.X = nextx + m.startx pos.X = nextx + m.startx
g.putpass(pos) g.putPassage(pos)
g.dig(nexty, nextx) g.dig(nexty, nextx)
} }
} }
// accntMaze accounts for maze exits (rooms.c accnt_maze). // accountMaze accounts for maze exits (rooms.c accnt_maze).
func (g *RogueGame) accntMaze(y, x, ny, nx int) { func (g *RogueGame) accountMaze(y, x, ny, nx int) {
sp := &g.maze.maze[y][x] sp := &g.maze.maze[y][x]
for i := range sp.nexits { for i := range sp.nexits {
if sp.exits[i].Y == ny && sp.exits[i].X == nx { 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). // randomPos picks a random spot in a room (rooms.c rnd_pos).
func (g *RogueGame) rndPos(rp *Room) Coord { func (g *RogueGame) randomPos(rp *Room) Coord {
var cp Coord var cp Coord
cp.X = rp.Pos.X + g.rnd(rp.Max.X-2) + 1 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 { if pickroom {
rp = &g.Level.Rooms[g.rndRoom()] rp = &g.Level.Rooms[g.randomRoom()]
compchar = Floor compchar = Floor
if rp.Flags.Has(Maze) { 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) pp := g.Level.At(cp.Y, cp.X)
if monst { if monst {
@@ -321,7 +321,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co
// enter_room). // enter_room).
func (g *RogueGame) enterRoom(cp Coord) { func (g *RogueGame) enterRoom(cp Coord) {
p := &g.Player p := &g.Player
rp := g.roomin(cp) rp := g.roomIn(cp)
p.Room = rp p.Room = rp
g.doorOpen(rp) g.doorOpen(rp)

View File

@@ -449,12 +449,14 @@ func (g *RogueGame) applySnapshot(st *SaveState) {
g.scr.Std.SetContents(st.Screen) g.scr.Std.SetContents(st.Screen)
} }
// saveGame implements the "save game" command (save.c save_game). The C // saveGame implements the "save game" command (save.c save_game). The
// goto over/gotfile flow becomes the useDefault flag. // labeled prompt loop and the useDefault flag stand in for the C
// goto over/gotfile flow.
func (g *RogueGame) saveGame() { func (g *RogueGame) saveGame() {
g.Msgs.Mpos = 0 g.Msgs.Mpos = 0
over: prompt:
for {
useDefault := false useDefault := false
if g.FileName != "" { if g.FileName != "" {
@@ -522,7 +524,7 @@ over:
} }
if c == 'n' || c == 'N' { if c == 'n' || c == 'N' {
goto over continue prompt // the C goto over: start again
} }
g.msg("Please answer Y or N") g.msg("Please answer Y or N")
@@ -541,7 +543,8 @@ over:
continue continue
} }
break break prompt
}
} }
g.myExit() g.myExit()
@@ -615,6 +618,7 @@ func Restore(path string, cfg Config) (*RogueGame, error) {
restored: true, restored: true,
} }
g.scr = NewScreen(cfg.Term) g.scr = NewScreen(cfg.Term)
g.Msgs.attach(g.scr, g.look, g.readchar)
g.applySnapshot(&st) g.applySnapshot(&st)
// defeat multiple restarting from the same place // defeat multiple restarting from the same place

View File

@@ -7,8 +7,8 @@ package game
func (g *RogueGame) readScroll() { func (g *RogueGame) readScroll() {
p := &g.Player p := &g.Player
obj := g.getItem("read", KindScroll) obj, ok := g.promptPackItem("read", KindScroll)
if obj == nil { if !ok {
return return
} }
@@ -104,7 +104,7 @@ func (g *RogueGame) readScroll() {
// Or anything else nasty // Or anything else nasty
if ch := g.Level.VisibleChar(y, x); stepOk(ch) { if ch := g.Level.VisibleChar(y, x); stepOk(ch) {
if ch == Scroll { if ch == Scroll {
if fo := g.findObj(y, x); fo != nil && fo.ScrollKind() == ScrollScareMonster { if fo := g.Level.ObjectAt(y, x); fo != nil && fo.ScrollKind() == ScrollScareMonster {
continue continue
} }
} }

View File

@@ -14,8 +14,8 @@ const (
func (g *RogueGame) doZap() { func (g *RogueGame) doZap() {
p := &g.Player p := &g.Player
obj := g.getItem("zap with", KindWand) obj, ok := g.promptPackItem("zap with", KindWand)
if obj == nil { if !ok {
return return
} }
@@ -80,12 +80,12 @@ func (g *RogueGame) doZap() {
case WandInvisibility: case WandInvisibility:
tp.Flags.Set(Invisible) tp.Flags.Set(Invisible)
if g.cansee(y, x) { if g.canSee(y, x) {
g.mvaddch(y, x, tp.OldCh) g.mvaddch(y, x, tp.OldCh)
} }
case WandPolymorph: case WandPolymorph:
pp := tp.Pack pp := tp.Pack
detachMon(&g.Level.Monsters, tp) g.Level.RemoveMonster(tp)
if g.seeMonst(tp) { if g.seeMonst(tp) {
g.mvaddch(y, x, g.Level.Char(y, x)) g.mvaddch(y, x, g.Level.Char(y, x))
@@ -186,7 +186,7 @@ func (g *RogueGame) doZap() {
g.Delta.Y = y g.Delta.Y = y
g.Delta.X = x g.Delta.X = x
g.runto(g.Delta) g.runTo(g.Delta)
} }
case WandLightning, WandFire, WandCold: case WandLightning, WandFire, WandCold:
var name string var name string
@@ -243,7 +243,7 @@ func (g *RogueGame) drain() {
if mp.Stats.HP -= cnt; mp.Stats.HP <= 0 { if mp.Stats.HP -= cnt; mp.Stats.HP <= 0 {
g.killed(mp, g.seeMonst(mp)) g.killed(mp, g.seeMonst(mp))
} else { } 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' { } else if ch != 'M' || tp.Disguise == 'M' {
if fromHero { if fromHero {
g.runto(pos) g.runTo(pos)
} }
if g.Options.Terse { if g.Options.Terse {

View File

@@ -94,7 +94,7 @@ type gameData struct {
initWeaps [NumWeaponTypes]weaponSetup initWeaps [NumWeaponTypes]weaponSetup
// pActions is potions.c p_actions[]. The P_SEEINVIS message is dynamic // pActions is potions.c p_actions[]. The P_SEEINVIS message is dynamic
// (it names the fruit) and is computed in doPot. // (it names the fruit) and is computed in applyPotionFuse.
pActions [NumPotionTypes]pact pActions [NumPotionTypes]pact
// idType maps identify scrolls to the kind of item they identify // idType maps identify scrolls to the kind of item they identify

View File

@@ -9,7 +9,7 @@ import (
// invName returns the name of something as it would appear in an inventory // invName returns the name of something as it would appear in an inventory
// (things.c inv_name). // (things.c inv_name).
func (g *RogueGame) invName(obj *Object, drop bool) string { func (g *RogueGame) inventoryName(obj *Object, drop bool) string {
var pb strings.Builder var pb strings.Builder
which := obj.Which which := obj.Which
@@ -142,8 +142,8 @@ func (g *RogueGame) dropIt() {
return return
} }
obj := g.getItem("drop", KindNone) obj, ok := g.promptPackItem("drop", KindNone)
if obj == nil { if !ok {
return return
} }
@@ -153,7 +153,7 @@ func (g *RogueGame) dropIt() {
obj = g.leavePack(obj, true, !obj.Kind.MergesInPack()) obj = g.leavePack(obj, true, !obj.Kind.MergesInPack())
// Link it into the level object list // Link it into the level object list
attachObj(&g.Level.Objects, obj) g.Level.AddObject(obj)
g.Level.SetChar(p.Pos.Y, p.Pos.X, obj.Kind.Glyph()) g.Level.SetChar(p.Pos.Y, p.Pos.X, obj.Kind.Glyph())
g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Set(FDropped) g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Set(FDropped)
@@ -162,7 +162,7 @@ func (g *RogueGame) dropIt() {
g.HasAmulet = false g.HasAmulet = false
} }
g.msg("dropped %s", g.invName(obj, true)) g.msg("dropped %s", g.inventoryName(obj, true))
} }
// dropCheck does special checks for dropping or unwielding|unwearing| // dropCheck does special checks for dropping or unwielding|unwearing|
@@ -201,7 +201,7 @@ func (g *RogueGame) dropCheck(obj *Object) bool {
switch obj.RingKind() { switch obj.RingKind() {
case RingAddStrength: case RingAddStrength:
g.chgStr(-obj.Bonus) g.changeStrength(-obj.Bonus)
case RingSeeInvisible: case RingSeeInvisible:
g.unsee(0) g.unsee(0)
g.Extinguish(DUnsee) g.Extinguish(DUnsee)
@@ -392,7 +392,7 @@ func (g *RogueGame) printDisc(typ byte) {
if info[order[i]].Know || info[order[i]].Guess != "" { if info[order[i]].Know || info[order[i]].Guess != "" {
obj.Kind = objectKindForGlyph(typ) obj.Kind = objectKindForGlyph(typ)
obj.Which = order[i] obj.Which = order[i]
g.addLine("%s", g.invName(&obj, false)) g.addLine("%s", g.inventoryName(&obj, false))
numFound++ numFound++
} }

View File

@@ -9,8 +9,8 @@ const noWeapon WeaponKind = -1
// missile fires a missile in a given direction (weapons.c missile). // missile fires a missile in a given direction (weapons.c missile).
func (g *RogueGame) missile(ydelta, xdelta int) { func (g *RogueGame) missile(ydelta, xdelta int) {
// Get which thing we are hurling // Get which thing we are hurling
obj := g.getItem("throw", KindWeapon) obj, ok := g.promptPackItem("throw", KindWeapon)
if obj == nil { if !ok {
return return
} }
@@ -36,7 +36,7 @@ func (g *RogueGame) doMotion(obj *Object, ydelta, xdelta int) {
obj.Pos = p.Pos obj.Pos = p.Pos
for { for {
// Erase the old one // 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) ch := g.Level.Char(obj.Pos.Y, obj.Pos.X)
if ch == Floor && !g.showFloor() { if ch == Floor && !g.showFloor() {
ch = ' ' ch = ' '
@@ -51,7 +51,7 @@ func (g *RogueGame) doMotion(obj *Object, ydelta, xdelta int) {
ch := g.Level.VisibleChar(obj.Pos.Y, obj.Pos.X) ch := g.Level.VisibleChar(obj.Pos.Y, obj.Pos.X)
if stepOk(ch) && ch != Door { if stepOk(ch) && ch != Door {
// It hasn't hit anything yet, so display it if it's alright. // 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.mvaddch(obj.Pos.Y, obj.Pos.X, obj.Kind.Glyph())
g.refresh() g.refresh()
} }
@@ -70,7 +70,7 @@ func (g *RogueGame) fall(obj *Object, pr bool) {
pp.Ch = obj.Kind.Glyph() pp.Ch = obj.Kind.Glyph()
obj.Pos = fpos obj.Pos = fpos
if g.cansee(fpos.Y, fpos.X) { if g.canSee(fpos.Y, fpos.X) {
if pp.Monst != nil { if pp.Monst != nil {
pp.Monst.OldCh = obj.Kind.Glyph() pp.Monst.OldCh = obj.Kind.Glyph()
} else { } else {
@@ -78,7 +78,7 @@ func (g *RogueGame) fall(obj *Object, pr bool) {
} }
} }
attachObj(&g.Level.Objects, obj) g.Level.AddObject(obj)
return return
} }
@@ -113,8 +113,8 @@ func (g *RogueGame) wield() {
p.CurWeapon = oweapon p.CurWeapon = oweapon
obj := g.getItem("wield", KindWeapon) obj, ok := g.promptPackItem("wield", KindWeapon)
if obj == nil { if !ok {
g.After = false g.After = false
return return
@@ -133,7 +133,7 @@ func (g *RogueGame) wield() {
return return
} }
sp := g.invName(obj, true) sp := g.inventoryName(obj, true)
p.CurWeapon = obj p.CurWeapon = obj
if !g.Options.Terse { if !g.Options.Terse {

View File

@@ -119,7 +119,7 @@ func (g *RogueGame) whatis(insist bool, kind ObjectKind) {
var obj *Object var obj *Object
for { for {
obj = g.getItem("identify", kind) obj, _ = g.promptPackItem("identify", kind)
if !insist { if !insist {
break break
@@ -161,7 +161,7 @@ func (g *RogueGame) whatis(insist bool, kind ObjectKind) {
setKnow(obj, g.Items.Rings[:]) setKnow(obj, g.Items.Rings[:])
} }
g.msg("%s", g.invName(obj, false)) g.msg("%s", g.inventoryName(obj, false))
} }
// setKnow sets things up when we really know what a thing is (wizard.c // setKnow sets things up when we really know what a thing is (wizard.c
@@ -181,7 +181,7 @@ func (g *RogueGame) teleport() {
g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt()) g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt())
c, _ := g.findFloor(true) c, _ := g.findFloor(true)
if g.roomin(c) != p.Room { if g.roomIn(c) != p.Room {
g.leaveRoom(p.Pos) g.leaveRoom(p.Pos)
p.Pos = c p.Pos = c
g.enterRoom(p.Pos) g.enterRoom(p.Pos)