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.
This commit is contained in:
289
game/move.go
289
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,
|
||||
|
||||
Reference in New Issue
Block a user