Decompose look and promptDirection (refactor step 7)
look's nine-square scan splits into lookAround/lookCell with a lookScan state struct and guard helpers (lookSkips, lookForeignPassage, lookDiagonalBlocked, lookCellChar, lookShow, lookRunCheck, atRunEdge); promptDirection gains deltaFor and confuseDirection. misc.go is complexity-clean. Behavior and RNG call order unchanged.
This commit is contained in:
436
game/misc.go
436
game/misc.go
@@ -4,11 +4,23 @@ package game
|
||||
// and small utilities. call_it arrives with the scroll/potion phase (it
|
||||
// needs the get_str line editor).
|
||||
|
||||
// lookScan carries the state of one look() glance while it examines the
|
||||
// nine squares around the hero.
|
||||
type lookScan struct {
|
||||
hero Coord
|
||||
pch byte // map character under the hero
|
||||
pfl PlaceFlags // map flags under the hero
|
||||
wakeup bool
|
||||
doorStop bool // door-stop checking applies (mid-run)
|
||||
sy, sx, ey, ex int
|
||||
sumhero, diffhero int
|
||||
passcount int
|
||||
}
|
||||
|
||||
// look takes a quick glance all around the player (misc.c look).
|
||||
func (g *RogueGame) look(wakeup bool) {
|
||||
p := &g.Player
|
||||
hero := p.Pos
|
||||
passcount := 0
|
||||
rp := p.Room
|
||||
|
||||
if g.Oldpos != hero {
|
||||
@@ -17,151 +29,28 @@ func (g *RogueGame) look(wakeup bool) {
|
||||
g.Oldrp = rp
|
||||
}
|
||||
|
||||
ey := hero.Y + 1
|
||||
ex := hero.X + 1
|
||||
sx := hero.X - 1
|
||||
sy := hero.Y - 1
|
||||
s := lookScan{
|
||||
hero: hero,
|
||||
wakeup: wakeup,
|
||||
sy: hero.Y - 1,
|
||||
sx: hero.X - 1,
|
||||
ey: hero.Y + 1,
|
||||
ex: hero.X + 1,
|
||||
}
|
||||
|
||||
sumhero, diffhero := 0, 0
|
||||
if g.DoorStop && !g.Firstmove && g.Running {
|
||||
sumhero = hero.Y + hero.X
|
||||
diffhero = hero.Y - hero.X
|
||||
s.doorStop = g.DoorStop && !g.Firstmove
|
||||
if s.doorStop && g.Running {
|
||||
s.sumhero = hero.Y + hero.X
|
||||
s.diffhero = hero.Y - hero.X
|
||||
}
|
||||
|
||||
pp := g.Level.At(hero.Y, hero.X)
|
||||
pch := pp.Ch
|
||||
pfl := pp.Flags
|
||||
s.pch = pp.Ch
|
||||
s.pfl = pp.Flags
|
||||
|
||||
for y := sy; y <= ey; y++ {
|
||||
if y <= 0 || y >= NumLines-1 {
|
||||
continue
|
||||
}
|
||||
g.lookAround(&s)
|
||||
|
||||
for x := sx; x <= ex; x++ {
|
||||
if x < 0 || x >= NumCols {
|
||||
continue
|
||||
}
|
||||
|
||||
if !p.On(Blind) {
|
||||
if y == hero.Y && x == hero.X {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
pp := g.Level.At(y, x)
|
||||
|
||||
ch := pp.Ch
|
||||
if ch == ' ' { // nothing need be done with a ' '
|
||||
continue
|
||||
}
|
||||
|
||||
fp := &pp.Flags
|
||||
if pch != Door && ch != Door {
|
||||
if (pfl & FPassage) != (*fp & FPassage) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (fp.Has(FPassage) || ch == Door) && (pfl.Has(FPassage) || pch == Door) {
|
||||
if hero.X != x && hero.Y != y &&
|
||||
!stepOk(g.Level.Char(y, hero.X)) && !stepOk(g.Level.Char(hero.Y, x)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
tp := pp.Monst
|
||||
|
||||
switch {
|
||||
case tp == nil:
|
||||
ch = g.tripCh(y, x, ch)
|
||||
case p.On(SenseMonsters) && tp.On(Invisible):
|
||||
if g.DoorStop && !g.Firstmove {
|
||||
g.Running = false
|
||||
}
|
||||
|
||||
continue
|
||||
default:
|
||||
if wakeup {
|
||||
g.wakeMonster(y, x)
|
||||
}
|
||||
|
||||
if g.seeMonst(tp) {
|
||||
if p.On(Hallucinating) {
|
||||
ch = g.randomMonsterLetter()
|
||||
} else {
|
||||
ch = tp.Disguise
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if p.On(Blind) && (y != hero.Y || x != hero.X) {
|
||||
continue
|
||||
}
|
||||
|
||||
g.move(y, x)
|
||||
|
||||
if p.Room.Flags.Has(Dark) && !g.Options.SeeFloor && ch == Floor {
|
||||
ch = ' '
|
||||
}
|
||||
|
||||
if tp != nil || ch != g.inch() {
|
||||
g.addch(ch)
|
||||
}
|
||||
|
||||
if g.DoorStop && !g.Firstmove && g.Running {
|
||||
switch g.RunCh {
|
||||
case 'h':
|
||||
if x == ex {
|
||||
continue
|
||||
}
|
||||
case 'j':
|
||||
if y == sy {
|
||||
continue
|
||||
}
|
||||
case 'k':
|
||||
if y == ey {
|
||||
continue
|
||||
}
|
||||
case 'l':
|
||||
if x == sx {
|
||||
continue
|
||||
}
|
||||
case 'y':
|
||||
if (y+x)-sumhero >= 1 {
|
||||
continue
|
||||
}
|
||||
case 'u':
|
||||
if (y-x)-diffhero >= 1 {
|
||||
continue
|
||||
}
|
||||
case 'n':
|
||||
if (y+x)-sumhero <= -1 {
|
||||
continue
|
||||
}
|
||||
case 'b':
|
||||
if (y-x)-diffhero <= -1 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
switch ch {
|
||||
case Door:
|
||||
if x == hero.X || y == hero.Y {
|
||||
g.Running = false
|
||||
}
|
||||
case Passage:
|
||||
if x == hero.X || y == hero.Y {
|
||||
passcount++
|
||||
}
|
||||
case Floor, '|', '-', ' ':
|
||||
default:
|
||||
g.Running = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if g.DoorStop && !g.Firstmove && passcount > 1 {
|
||||
if s.doorStop && s.passcount > 1 {
|
||||
g.Running = false
|
||||
}
|
||||
|
||||
@@ -170,6 +59,192 @@ func (g *RogueGame) look(wakeup bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// lookAround runs the nine-square scan of look().
|
||||
func (g *RogueGame) lookAround(s *lookScan) {
|
||||
for y := s.sy; y <= s.ey; y++ {
|
||||
if y <= 0 || y >= NumLines-1 {
|
||||
continue
|
||||
}
|
||||
|
||||
for x := s.sx; x <= s.ex; x++ {
|
||||
if x < 0 || x >= NumCols {
|
||||
continue
|
||||
}
|
||||
|
||||
g.lookCell(s, y, x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lookCell examines one square around the hero: visibility rules, trip
|
||||
// and monster rendering, drawing, and run-stop checks (the loop body of
|
||||
// misc.c look).
|
||||
func (g *RogueGame) lookCell(s *lookScan, y, x int) {
|
||||
pp := g.Level.At(y, x)
|
||||
if g.lookSkips(s, pp, y, x) {
|
||||
return
|
||||
}
|
||||
|
||||
tp := pp.Monst
|
||||
|
||||
ch, skip := g.lookCellChar(s, tp, y, x, pp.Ch)
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
if !g.lookShow(s, tp, ch, y, x) {
|
||||
return
|
||||
}
|
||||
|
||||
if s.doorStop && g.Running {
|
||||
g.lookRunCheck(s, ch, y, x)
|
||||
}
|
||||
}
|
||||
|
||||
// lookSkips reports whether look ignores this square entirely: the
|
||||
// hero's own square when sighted, blank rock, passage squares of
|
||||
// another network, and diagonals the hero could not step to (the guard
|
||||
// chain of the misc.c look loop).
|
||||
func (g *RogueGame) lookSkips(s *lookScan, pp *Place, y, x int) bool {
|
||||
if !g.Player.On(Blind) && y == s.hero.Y && x == s.hero.X {
|
||||
return true
|
||||
}
|
||||
|
||||
if pp.Ch == ' ' { // nothing need be done with a ' '
|
||||
return true
|
||||
}
|
||||
|
||||
return lookForeignPassage(s, pp.Flags, pp.Ch) ||
|
||||
g.lookDiagonalBlocked(s, pp.Flags, pp.Ch, y, x)
|
||||
}
|
||||
|
||||
// lookForeignPassage hides passage squares belonging to a different
|
||||
// passage network than the hero's (misc.c look).
|
||||
func lookForeignPassage(s *lookScan, fp PlaceFlags, ch byte) bool {
|
||||
if s.pch != Door && ch != Door {
|
||||
return (s.pfl & FPassage) != (fp & FPassage)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// lookDiagonalBlocked hides diagonal door/passage squares the hero could
|
||||
// not actually step to (misc.c look).
|
||||
func (g *RogueGame) lookDiagonalBlocked(s *lookScan, fp PlaceFlags, ch byte, y, x int) bool {
|
||||
if !fp.Has(FPassage) && ch != Door {
|
||||
return false
|
||||
}
|
||||
|
||||
if !s.pfl.Has(FPassage) && s.pch != Door {
|
||||
return false
|
||||
}
|
||||
|
||||
return s.hero.X != x && s.hero.Y != y &&
|
||||
!stepOk(g.Level.Char(y, s.hero.X)) && !stepOk(g.Level.Char(s.hero.Y, x))
|
||||
}
|
||||
|
||||
// lookShow draws the square if it changed; it reports false when a
|
||||
// blind hero cannot see it at all (the draw part of the look loop).
|
||||
func (g *RogueGame) lookShow(s *lookScan, tp *Monster, ch byte, y, x int) bool {
|
||||
p := &g.Player
|
||||
if p.On(Blind) && (y != s.hero.Y || x != s.hero.X) {
|
||||
return false
|
||||
}
|
||||
|
||||
g.move(y, x)
|
||||
|
||||
if p.Room.Flags.Has(Dark) && !g.Options.SeeFloor && ch == Floor {
|
||||
ch = ' '
|
||||
}
|
||||
|
||||
if tp != nil || ch != g.inch() {
|
||||
g.addch(ch)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// lookCellChar picks what the square shows: trip rendering for empty
|
||||
// squares, waking and disguises for monsters. skip means the square is
|
||||
// not drawn at all (the monster switch of the look loop).
|
||||
func (g *RogueGame) lookCellChar(s *lookScan, tp *Monster, y, x int, ch byte) (byte, bool) {
|
||||
p := &g.Player
|
||||
|
||||
switch {
|
||||
case tp == nil:
|
||||
return g.tripCh(y, x, ch), false
|
||||
case p.On(SenseMonsters) && tp.On(Invisible):
|
||||
if g.DoorStop && !g.Firstmove {
|
||||
g.Running = false
|
||||
}
|
||||
|
||||
return ch, true
|
||||
default:
|
||||
if s.wakeup {
|
||||
g.wakeMonster(y, x)
|
||||
}
|
||||
|
||||
if g.seeMonst(tp) {
|
||||
if p.On(Hallucinating) {
|
||||
return g.randomMonsterLetter(), false
|
||||
}
|
||||
|
||||
return tp.Disguise, false
|
||||
}
|
||||
|
||||
return ch, false
|
||||
}
|
||||
}
|
||||
|
||||
// lookRunCheck decides whether what this square shows should stop a run
|
||||
// (the DoorStop tail of the misc.c look loop). Squares on the running
|
||||
// edge are ignored.
|
||||
func (g *RogueGame) lookRunCheck(s *lookScan, ch byte, y, x int) {
|
||||
if s.atRunEdge(g.RunCh, y, x) {
|
||||
return
|
||||
}
|
||||
|
||||
switch ch {
|
||||
case Door:
|
||||
if x == s.hero.X || y == s.hero.Y {
|
||||
g.Running = false
|
||||
}
|
||||
case Passage:
|
||||
if x == s.hero.X || y == s.hero.Y {
|
||||
s.passcount++
|
||||
}
|
||||
case Floor, '|', '-', ' ':
|
||||
default:
|
||||
g.Running = false
|
||||
}
|
||||
}
|
||||
|
||||
// atRunEdge reports whether (y, x) sits on the leading edge of the run
|
||||
// direction, where door-stop checking does not apply (the first RunCh
|
||||
// switch of the misc.c look loop).
|
||||
func (s *lookScan) atRunEdge(runCh byte, y, x int) bool {
|
||||
switch runCh {
|
||||
case 'h':
|
||||
return x == s.ex
|
||||
case 'j':
|
||||
return y == s.sy
|
||||
case 'k':
|
||||
return y == s.ey
|
||||
case 'l':
|
||||
return x == s.sx
|
||||
case 'y':
|
||||
return (y+x)-s.sumhero >= 1
|
||||
case 'u':
|
||||
return (y-x)-s.diffhero >= 1
|
||||
case 'n':
|
||||
return (y+x)-s.sumhero <= -1
|
||||
case 'b':
|
||||
return (y-x)-s.diffhero <= -1
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// tripCh returns the character for this space, taking into account whether
|
||||
// or not the player is tripping (misc.c trip_ch).
|
||||
func (g *RogueGame) tripCh(y, x int, ch byte) byte {
|
||||
@@ -409,40 +484,22 @@ func (g *RogueGame) promptDirection() bool {
|
||||
}
|
||||
|
||||
for {
|
||||
gotit := true
|
||||
|
||||
switch g.DirCh = g.readchar(); g.DirCh {
|
||||
case 'h', 'H':
|
||||
g.Delta = Coord{X: -1, Y: 0}
|
||||
case 'j', 'J':
|
||||
g.Delta = Coord{X: 0, Y: 1}
|
||||
case 'k', 'K':
|
||||
g.Delta = Coord{X: 0, Y: -1}
|
||||
case 'l', 'L':
|
||||
g.Delta = Coord{X: 1, Y: 0}
|
||||
case 'y', 'Y':
|
||||
g.Delta = Coord{X: -1, Y: -1}
|
||||
case 'u', 'U':
|
||||
g.Delta = Coord{X: 1, Y: -1}
|
||||
case 'b', 'B':
|
||||
g.Delta = Coord{X: -1, Y: 1}
|
||||
case 'n', 'N':
|
||||
g.Delta = Coord{X: 1, Y: 1}
|
||||
case Escape:
|
||||
g.DirCh = g.readchar()
|
||||
if g.DirCh == Escape {
|
||||
g.LastDir = 0
|
||||
g.resetLast()
|
||||
|
||||
return false
|
||||
default:
|
||||
g.Msgs.Mpos = 0
|
||||
g.msg("%s", prompt)
|
||||
|
||||
gotit = false
|
||||
}
|
||||
|
||||
if gotit {
|
||||
if d, ok := deltaFor(g.DirCh); ok {
|
||||
g.Delta = d
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
g.Msgs.Mpos = 0
|
||||
g.msg("%s", prompt)
|
||||
}
|
||||
|
||||
g.DirCh = toLower(g.DirCh)
|
||||
@@ -451,14 +508,7 @@ func (g *RogueGame) promptDirection() bool {
|
||||
}
|
||||
|
||||
if g.Player.On(Confused) && g.rnd(5) == 0 {
|
||||
for {
|
||||
g.Delta.Y = g.rnd(3) - 1
|
||||
|
||||
g.Delta.X = g.rnd(3) - 1
|
||||
if g.Delta.Y != 0 || g.Delta.X != 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
g.confuseDirection()
|
||||
}
|
||||
|
||||
g.Msgs.Mpos = 0
|
||||
@@ -466,6 +516,44 @@ func (g *RogueGame) promptDirection() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// confuseDirection randomizes the chosen direction for a confused hero
|
||||
// (the ISHUH tail of misc.c get_dir).
|
||||
func (g *RogueGame) confuseDirection() {
|
||||
for {
|
||||
g.Delta.Y = g.rnd(3) - 1
|
||||
|
||||
g.Delta.X = g.rnd(3) - 1
|
||||
if g.Delta.Y != 0 || g.Delta.X != 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deltaFor maps a direction key to its movement delta; ok is false for
|
||||
// keys that are not directions (the switch of misc.c get_dir).
|
||||
func deltaFor(ch byte) (Coord, bool) {
|
||||
switch ch {
|
||||
case 'h', 'H':
|
||||
return Coord{X: -1, Y: 0}, true
|
||||
case 'j', 'J':
|
||||
return Coord{X: 0, Y: 1}, true
|
||||
case 'k', 'K':
|
||||
return Coord{X: 0, Y: -1}, true
|
||||
case 'l', 'L':
|
||||
return Coord{X: 1, Y: 0}, true
|
||||
case 'y', 'Y':
|
||||
return Coord{X: -1, Y: -1}, true
|
||||
case 'u', 'U':
|
||||
return Coord{X: 1, Y: -1}, true
|
||||
case 'b', 'B':
|
||||
return Coord{X: -1, Y: 1}, true
|
||||
case 'n', 'N':
|
||||
return Coord{X: 1, Y: 1}, true
|
||||
}
|
||||
|
||||
return Coord{}, false
|
||||
}
|
||||
|
||||
// callIt calls an object something after use (misc.c call_it).
|
||||
func (g *RogueGame) callIt(info *ObjInfo) {
|
||||
if info.Know {
|
||||
|
||||
Reference in New Issue
Block a user