Decompose command.go; command keys become a handler table (step 7)
The ordinary command keys move into gameData.commandHandlers (method expressions and small literals); dispatchKey keeps only re-dispatching prefixes (runCommand/fightCommand/repeatCommand/moveOnCommand) and the wizard fallthrough. command() splits into playTurn/turnUpkeep/ readCommand/executeCommand/countPrefix/ringTurnEffects; search gains searchSpot/searchFloor; help gains helpOne/helpAll/helpLines; call gains callTarget/callPrelude; wizardCommand splits in two plus wizardKit; uLevel and current flatten to early returns. command.go is complexity-clean. Behavior and RNG call order unchanged.
This commit is contained in:
784
game/command.go
784
game/command.go
@@ -16,6 +16,70 @@ func (g *RogueGame) command() {
|
||||
g.DoFuses(Before)
|
||||
|
||||
for ; ntimes > 0; ntimes-- {
|
||||
g.playTurn()
|
||||
|
||||
if !g.After {
|
||||
ntimes++
|
||||
}
|
||||
}
|
||||
|
||||
g.DoDaemons(After)
|
||||
g.DoFuses(After)
|
||||
|
||||
g.ringTurnEffects(Left)
|
||||
g.ringTurnEffects(Right)
|
||||
}
|
||||
|
||||
// ringTurnEffects fires the per-turn ring powers on one hand: searching
|
||||
// searches, teleportation teleports on 1 turn in 50 (the tail of
|
||||
// command.c command).
|
||||
func (g *RogueGame) ringTurnEffects(hand int) {
|
||||
p := &g.Player
|
||||
if p.IsRing(hand, RingSearching) {
|
||||
g.search()
|
||||
} else if p.IsRing(hand, RingTeleportation) && g.rnd(50) == 0 {
|
||||
g.teleport()
|
||||
}
|
||||
}
|
||||
|
||||
// playTurn runs one full player turn: upkeep, one command, and pickup
|
||||
// (the body of the command.c command loop).
|
||||
func (g *RogueGame) playTurn() {
|
||||
p := &g.Player
|
||||
|
||||
g.turnUpkeep()
|
||||
|
||||
var ch byte
|
||||
|
||||
if g.NoCommand == 0 {
|
||||
ch = g.readCommand()
|
||||
} else {
|
||||
ch = '.'
|
||||
}
|
||||
|
||||
if g.NoCommand != 0 {
|
||||
if g.NoCommand--; g.NoCommand == 0 {
|
||||
p.Flags.Set(Awake)
|
||||
g.msg("you can move again")
|
||||
}
|
||||
} else {
|
||||
g.executeCommand(ch)
|
||||
}
|
||||
// If he ran into something to take, let him pick it up.
|
||||
if g.Take != 0 {
|
||||
g.pickUp(g.Take)
|
||||
}
|
||||
|
||||
if !g.Running {
|
||||
g.DoorStop = false
|
||||
}
|
||||
}
|
||||
|
||||
// turnUpkeep redraws and resets per-turn state before a command is read
|
||||
// (the top of the command.c command loop).
|
||||
func (g *RogueGame) turnUpkeep() {
|
||||
p := &g.Player
|
||||
|
||||
g.Again = false
|
||||
if g.HasHit {
|
||||
g.endmsg()
|
||||
@@ -47,59 +111,35 @@ func (g *RogueGame) command() {
|
||||
if g.Wizard {
|
||||
g.NoScore = true
|
||||
}
|
||||
}
|
||||
|
||||
var ch byte
|
||||
|
||||
if g.NoCommand == 0 {
|
||||
// readCommand picks the next command character: run/count repeats or
|
||||
// fresh input (the read block of command.c command).
|
||||
func (g *RogueGame) readCommand() byte {
|
||||
switch {
|
||||
case g.Running || g.ToDeath:
|
||||
ch = g.RunCh
|
||||
return g.RunCh
|
||||
case g.Count != 0:
|
||||
ch = g.countCh
|
||||
return g.countCh
|
||||
default:
|
||||
ch = g.readchar()
|
||||
ch := g.readchar()
|
||||
|
||||
g.MoveOn = false
|
||||
if g.Msgs.Mpos != 0 { // erase message if its there
|
||||
g.msg("")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ch = '.'
|
||||
}
|
||||
|
||||
if g.NoCommand != 0 {
|
||||
if g.NoCommand--; g.NoCommand == 0 {
|
||||
p.Flags.Set(Awake)
|
||||
g.msg("you can move again")
|
||||
return ch
|
||||
}
|
||||
} else {
|
||||
}
|
||||
|
||||
// executeCommand runs one read command with the repeat-count prefix and
|
||||
// the repeat-history bookkeeping (the execute block of command.c
|
||||
// command).
|
||||
func (g *RogueGame) executeCommand(ch byte) {
|
||||
// check for prefixes
|
||||
g.newCount = false
|
||||
if isDigit(ch) {
|
||||
g.Count = 0
|
||||
|
||||
g.newCount = true
|
||||
for isDigit(ch) {
|
||||
g.Count = min(g.Count*10+int(ch-'0'), 255)
|
||||
|
||||
ch = g.readchar()
|
||||
}
|
||||
|
||||
g.countCh = ch
|
||||
// turn off count for commands which don't make sense
|
||||
// to repeat
|
||||
switch ch {
|
||||
case CTRL('B'), CTRL('H'), CTRL('J'), CTRL('K'),
|
||||
CTRL('L'), CTRL('N'), CTRL('U'), CTRL('Y'),
|
||||
'.', 'a', 'b', 'h', 'j', 'k', 'l', 'm', 'n', 'q',
|
||||
'r', 's', 't', 'u', 'y', 'z', 'B', 'C', 'H', 'I',
|
||||
'J', 'K', 'L', 'N', 'U', 'Y',
|
||||
CTRL('D'), CTRL('A'):
|
||||
default:
|
||||
g.Count = 0
|
||||
}
|
||||
}
|
||||
ch = g.countPrefix(ch)
|
||||
// execute a command
|
||||
if g.Count != 0 && !g.Running {
|
||||
g.Count--
|
||||
@@ -120,45 +160,137 @@ func (g *RogueGame) command() {
|
||||
if !g.Running {
|
||||
g.DoorStop = false
|
||||
}
|
||||
}
|
||||
// If he ran into something to take, let him pick it up.
|
||||
if g.Take != 0 {
|
||||
g.pickUp(g.Take)
|
||||
}
|
||||
|
||||
// countPrefix consumes a numeric repeat-count prefix and returns the
|
||||
// command character that follows it (command.c).
|
||||
func (g *RogueGame) countPrefix(ch byte) byte {
|
||||
if !isDigit(ch) {
|
||||
return ch
|
||||
}
|
||||
|
||||
if !g.Running {
|
||||
g.DoorStop = false
|
||||
g.Count = 0
|
||||
|
||||
g.newCount = true
|
||||
for isDigit(ch) {
|
||||
g.Count = min(g.Count*10+int(ch-'0'), 255)
|
||||
|
||||
ch = g.readchar()
|
||||
}
|
||||
|
||||
if !g.After {
|
||||
ntimes++
|
||||
}
|
||||
g.countCh = ch
|
||||
// turn off count for commands which don't make sense to repeat
|
||||
switch ch {
|
||||
case CTRL('B'), CTRL('H'), CTRL('J'), CTRL('K'),
|
||||
CTRL('L'), CTRL('N'), CTRL('U'), CTRL('Y'),
|
||||
'.', 'a', 'b', 'h', 'j', 'k', 'l', 'm', 'n', 'q',
|
||||
'r', 's', 't', 'u', 'y', 'z', 'B', 'C', 'H', 'I',
|
||||
'J', 'K', 'L', 'N', 'U', 'Y',
|
||||
CTRL('D'), CTRL('A'):
|
||||
default:
|
||||
g.Count = 0
|
||||
}
|
||||
|
||||
g.DoDaemons(After)
|
||||
g.DoFuses(After)
|
||||
return ch
|
||||
}
|
||||
|
||||
if p.IsRing(Left, RingSearching) {
|
||||
g.search()
|
||||
} else if p.IsRing(Left, RingTeleportation) && g.rnd(50) == 0 {
|
||||
g.teleport()
|
||||
// dispatch runs one command character, re-running dispatchKey when it
|
||||
// asks (the C `goto over` re-dispatch in command.c).
|
||||
func (g *RogueGame) dispatch(ch byte) {
|
||||
for {
|
||||
newCh, again := g.dispatchKey(ch)
|
||||
if !again {
|
||||
return
|
||||
}
|
||||
|
||||
if p.IsRing(Right, RingSearching) {
|
||||
g.search()
|
||||
} else if p.IsRing(Right, RingTeleportation) && g.rnd(50) == 0 {
|
||||
g.teleport()
|
||||
ch = newCh
|
||||
}
|
||||
}
|
||||
|
||||
// dispatch runs one command character (the big switch in command.c; the
|
||||
// loop stands in for its `goto over` re-dispatch).
|
||||
func (g *RogueGame) dispatch(ch byte) {
|
||||
// dispatchKey runs one command character (the big switch of command.c).
|
||||
// A true second result asks dispatch to run once more with the returned
|
||||
// key.
|
||||
func (g *RogueGame) dispatchKey(ch byte) (byte, bool) {
|
||||
if h, ok := g.data.commandHandlers[ch]; ok {
|
||||
h(g)
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
switch ch {
|
||||
case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'),
|
||||
CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'):
|
||||
return g.runCommand(ch)
|
||||
case 'F', 'f':
|
||||
return g.fightCommand(ch)
|
||||
case 'a':
|
||||
return g.repeatCommand()
|
||||
case 'm':
|
||||
return g.moveOnCommand()
|
||||
default:
|
||||
g.After = false
|
||||
if g.Wizard {
|
||||
g.wizardCommand(ch)
|
||||
} else {
|
||||
g.illcom(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// runCommand handles the ctrl-direction run-until-adjacent prefix; the
|
||||
// returned key re-dispatches as the matching run command (command.c).
|
||||
func (g *RogueGame) runCommand(ch byte) (byte, bool) {
|
||||
if !g.Player.On(Blind) {
|
||||
g.DoorStop = true
|
||||
g.Firstmove = true
|
||||
}
|
||||
|
||||
if g.Count != 0 && !g.newCount {
|
||||
ch = g.direction
|
||||
} else {
|
||||
ch += 'A' - CTRL('A')
|
||||
g.direction = ch
|
||||
}
|
||||
|
||||
return ch, true // the C goto over: re-dispatch as the run command
|
||||
}
|
||||
|
||||
// repeatCommand handles 'a': replay the last command by re-dispatching
|
||||
// it (command.c).
|
||||
func (g *RogueGame) repeatCommand() (byte, bool) {
|
||||
if g.LastComm == 0 {
|
||||
g.msg("you haven't typed a command yet")
|
||||
g.After = false
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
g.Again = true
|
||||
|
||||
return g.LastComm, true // the C goto over: replay the last command
|
||||
}
|
||||
|
||||
// moveOnCommand handles 'm': move without picking up; the direction key
|
||||
// re-dispatches (command.c).
|
||||
func (g *RogueGame) moveOnCommand() (byte, bool) {
|
||||
g.MoveOn = true
|
||||
if !g.promptDirection() {
|
||||
g.After = false
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
g.countCh = g.DirCh
|
||||
|
||||
return g.DirCh, true // the C goto over: move onto it without pickup
|
||||
}
|
||||
|
||||
// pickupCommand handles ',': pick up whatever is here (command.c).
|
||||
func (g *RogueGame) pickupCommand() {
|
||||
p := &g.Player
|
||||
|
||||
for {
|
||||
switch ch {
|
||||
case ',':
|
||||
var found *Object
|
||||
|
||||
for _, obj := range g.Level.Objects {
|
||||
@@ -173,7 +305,10 @@ func (g *RogueGame) dispatch(ch byte) {
|
||||
if !g.levitCheck() {
|
||||
g.pickUp(found.Kind.Glyph())
|
||||
}
|
||||
} else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !g.Options.Terse {
|
||||
g.addmsgf("there is ")
|
||||
}
|
||||
@@ -185,57 +320,12 @@ func (g *RogueGame) dispatch(ch byte) {
|
||||
}
|
||||
|
||||
g.endmsg()
|
||||
}
|
||||
case '!':
|
||||
g.shell()
|
||||
case 'h':
|
||||
g.moveHero(0, -1)
|
||||
case 'j':
|
||||
g.moveHero(1, 0)
|
||||
case 'k':
|
||||
g.moveHero(-1, 0)
|
||||
case 'l':
|
||||
g.moveHero(0, 1)
|
||||
case 'y':
|
||||
g.moveHero(-1, -1)
|
||||
case 'u':
|
||||
g.moveHero(-1, 1)
|
||||
case 'b':
|
||||
g.moveHero(1, -1)
|
||||
case 'n':
|
||||
g.moveHero(1, 1)
|
||||
case 'H':
|
||||
g.startRun('h')
|
||||
case 'J':
|
||||
g.startRun('j')
|
||||
case 'K':
|
||||
g.startRun('k')
|
||||
case 'L':
|
||||
g.startRun('l')
|
||||
case 'Y':
|
||||
g.startRun('y')
|
||||
case 'U':
|
||||
g.startRun('u')
|
||||
case 'B':
|
||||
g.startRun('b')
|
||||
case 'N':
|
||||
g.startRun('n')
|
||||
case CTRL('H'), CTRL('J'), CTRL('K'), CTRL('L'),
|
||||
CTRL('Y'), CTRL('U'), CTRL('B'), CTRL('N'):
|
||||
if !p.On(Blind) {
|
||||
g.DoorStop = true
|
||||
g.Firstmove = true
|
||||
}
|
||||
}
|
||||
|
||||
if g.Count != 0 && !g.newCount {
|
||||
ch = g.direction
|
||||
} else {
|
||||
ch += 'A' - CTRL('A')
|
||||
g.direction = ch
|
||||
}
|
||||
|
||||
continue // the C goto over: re-dispatch as the run command
|
||||
case 'F', 'f':
|
||||
// fightCommand handles the f/F fight prefix; a true again asks dispatch
|
||||
// to rerun with the direction key (command.c).
|
||||
func (g *RogueGame) fightCommand(ch byte) (byte, bool) {
|
||||
p := &g.Player
|
||||
if ch == 'F' {
|
||||
g.Kamikaze = true
|
||||
}
|
||||
@@ -243,7 +333,7 @@ func (g *RogueGame) dispatch(ch byte) {
|
||||
if !g.promptDirection() {
|
||||
g.After = false
|
||||
|
||||
break
|
||||
return 0, false
|
||||
}
|
||||
|
||||
g.Delta.Y += p.Pos.Y
|
||||
@@ -264,103 +354,23 @@ func (g *RogueGame) dispatch(ch byte) {
|
||||
mp.Flags.Set(Targeted)
|
||||
|
||||
g.RunCh = g.DirCh
|
||||
ch = g.DirCh
|
||||
|
||||
continue // the C goto over: fight by running at it
|
||||
return g.DirCh, true // the C goto over: fight by running at it
|
||||
}
|
||||
case 't':
|
||||
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// identifyTrapCommand handles '^': name the trap in a direction
|
||||
// (command.c).
|
||||
func (g *RogueGame) identifyTrapCommand() {
|
||||
p := &g.Player
|
||||
|
||||
g.After = false
|
||||
if !g.promptDirection() {
|
||||
g.After = false
|
||||
} else {
|
||||
g.missile(g.Delta.Y, g.Delta.X)
|
||||
return
|
||||
}
|
||||
case 'a':
|
||||
if g.LastComm == 0 {
|
||||
g.msg("you haven't typed a command yet")
|
||||
g.After = false
|
||||
} else {
|
||||
ch = g.LastComm
|
||||
g.Again = true
|
||||
|
||||
continue // the C goto over: replay the last command
|
||||
}
|
||||
case 'q':
|
||||
g.quaff()
|
||||
case 'Q':
|
||||
g.After = false
|
||||
g.QComm = true
|
||||
g.quit(0)
|
||||
g.QComm = false
|
||||
case 'i':
|
||||
g.After = false
|
||||
g.inventory(p.Pack, 0)
|
||||
case 'I':
|
||||
g.After = false
|
||||
g.pickyInven()
|
||||
case 'd':
|
||||
g.dropIt()
|
||||
case 'r':
|
||||
g.readScroll()
|
||||
case 'e':
|
||||
g.eat()
|
||||
case 'w':
|
||||
g.wield()
|
||||
case 'W':
|
||||
g.wear()
|
||||
case 'T':
|
||||
g.takeOff()
|
||||
case 'P':
|
||||
g.ringOn()
|
||||
case 'R':
|
||||
g.ringOff()
|
||||
case 'o':
|
||||
g.option()
|
||||
g.After = false
|
||||
case 'c':
|
||||
g.call()
|
||||
g.After = false
|
||||
case '>':
|
||||
g.After = false
|
||||
g.dLevel()
|
||||
case '<':
|
||||
g.After = false
|
||||
g.uLevel()
|
||||
case '?':
|
||||
g.After = false
|
||||
g.help()
|
||||
case '/':
|
||||
g.After = false
|
||||
g.identify()
|
||||
case 's':
|
||||
g.search()
|
||||
case 'z':
|
||||
if g.promptDirection() {
|
||||
g.doZap()
|
||||
} else {
|
||||
g.After = false
|
||||
}
|
||||
case 'D':
|
||||
g.After = false
|
||||
g.discovered()
|
||||
case CTRL('P'):
|
||||
g.After = false
|
||||
g.msg("%s", g.Msgs.Huh)
|
||||
case CTRL('R'):
|
||||
g.After = false
|
||||
g.refresh()
|
||||
case 'v':
|
||||
g.After = false
|
||||
g.msg("version %s. (mctesq was here)", Release)
|
||||
case 'S':
|
||||
g.After = false
|
||||
g.saveGame()
|
||||
case '.':
|
||||
// rest command
|
||||
case ' ':
|
||||
g.After = false // "legal" illegal command
|
||||
case '^':
|
||||
g.After = false
|
||||
if g.promptDirection() {
|
||||
g.Delta.Y += p.Pos.Y
|
||||
g.Delta.X += p.Pos.X
|
||||
|
||||
@@ -378,47 +388,6 @@ func (g *RogueGame) dispatch(ch byte) {
|
||||
g.msg("%s", g.data.trName[*fp&FTrapMask])
|
||||
fp.Set(FSeen)
|
||||
}
|
||||
}
|
||||
case Escape: // escape
|
||||
g.DoorStop = false
|
||||
g.Count = 0
|
||||
g.After = false
|
||||
g.Again = false
|
||||
case 'm':
|
||||
g.MoveOn = true
|
||||
if !g.promptDirection() {
|
||||
g.After = false
|
||||
} else {
|
||||
ch = g.DirCh
|
||||
g.countCh = g.DirCh
|
||||
|
||||
continue // the C goto over: move onto it without pickup
|
||||
}
|
||||
case ')':
|
||||
g.current(p.CurWeapon, "wielding", "")
|
||||
case ']':
|
||||
g.current(p.CurArmor, "wearing", "")
|
||||
case '=':
|
||||
g.current(p.CurRing[Left], "wearing",
|
||||
g.chooseTerse("(L)", "on left hand"))
|
||||
g.current(p.CurRing[Right], "wearing",
|
||||
g.chooseTerse("(R)", "on right hand"))
|
||||
case '@':
|
||||
g.StatMsg = true
|
||||
g.status()
|
||||
g.StatMsg = false
|
||||
g.After = false
|
||||
default:
|
||||
g.After = false
|
||||
if g.Wizard {
|
||||
g.wizardCommand(ch)
|
||||
} else {
|
||||
g.illcom(ch)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// wizardCommand handles the MASTER debug commands (command.c).
|
||||
@@ -444,6 +413,17 @@ func (g *RogueGame) wizardCommand(ch byte) {
|
||||
g.NewLevel()
|
||||
case CTRL('F'):
|
||||
g.showMap()
|
||||
default:
|
||||
g.wizardDebugCommand(ch)
|
||||
}
|
||||
}
|
||||
|
||||
// wizardDebugCommand handles the rest of the MASTER debug commands
|
||||
// (command.c).
|
||||
func (g *RogueGame) wizardDebugCommand(ch byte) {
|
||||
p := &g.Player
|
||||
|
||||
switch ch {
|
||||
case CTRL('T'):
|
||||
g.teleport()
|
||||
case CTRL('E'):
|
||||
@@ -457,6 +437,19 @@ func (g *RogueGame) wizardCommand(ch byte) {
|
||||
item.Charges = 10000
|
||||
}
|
||||
case CTRL('I'):
|
||||
g.wizardKit()
|
||||
case '*':
|
||||
g.prList()
|
||||
default:
|
||||
g.illcom(ch)
|
||||
}
|
||||
}
|
||||
|
||||
// wizardKit levels the wizard up and equips him (the ^I case of the
|
||||
// command.c wizard switch).
|
||||
func (g *RogueGame) wizardKit() {
|
||||
p := &g.Player
|
||||
|
||||
for range 9 {
|
||||
g.raiseLevel()
|
||||
}
|
||||
@@ -476,11 +469,6 @@ func (g *RogueGame) wizardCommand(ch byte) {
|
||||
obj.Count = 1
|
||||
p.CurArmor = obj
|
||||
g.addPack(obj, true)
|
||||
case '*':
|
||||
g.prList()
|
||||
default:
|
||||
g.illcom(ch)
|
||||
}
|
||||
}
|
||||
|
||||
// illcom reports an illegal command (command.c illcom).
|
||||
@@ -514,9 +502,24 @@ func (g *RogueGame) search() {
|
||||
continue
|
||||
}
|
||||
|
||||
if g.searchSpot(y, x, probinc) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found {
|
||||
g.look(false)
|
||||
}
|
||||
}
|
||||
|
||||
// searchSpot tries to reveal one hidden thing next to the hero: a
|
||||
// secret door, a hidden trap, or a secret passage (the loop body of
|
||||
// command.c search). It reports whether something was found.
|
||||
func (g *RogueGame) searchSpot(y, x, probinc int) bool {
|
||||
fp := g.Level.FlagsAt(y, x)
|
||||
if fp.Has(FReal) {
|
||||
continue
|
||||
return false
|
||||
}
|
||||
|
||||
foundone := false
|
||||
@@ -532,24 +535,7 @@ func (g *RogueGame) search() {
|
||||
|
||||
foundone = true
|
||||
case Floor:
|
||||
if g.rnd(2+probinc) != 0 {
|
||||
break
|
||||
}
|
||||
|
||||
g.Level.SetChar(y, x, Trap)
|
||||
|
||||
if !g.Options.Terse {
|
||||
g.addmsgf("you found ")
|
||||
}
|
||||
|
||||
if p.On(Hallucinating) {
|
||||
g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)])
|
||||
} else {
|
||||
g.msg("%s", g.data.trName[*fp&FTrapMask])
|
||||
fp.Set(FSeen)
|
||||
}
|
||||
|
||||
foundone = true
|
||||
foundone = g.searchFloor(fp, y, x, probinc)
|
||||
case ' ':
|
||||
if g.rnd(3+probinc) != 0 {
|
||||
break
|
||||
@@ -561,19 +547,36 @@ func (g *RogueGame) search() {
|
||||
}
|
||||
|
||||
if foundone {
|
||||
found = true
|
||||
|
||||
fp.Set(FReal)
|
||||
|
||||
g.Count = 0
|
||||
g.Running = false
|
||||
}
|
||||
}
|
||||
|
||||
return foundone
|
||||
}
|
||||
|
||||
// searchFloor reveals a hidden trap underfoot and names it (the FLOOR
|
||||
// arm of command.c search).
|
||||
func (g *RogueGame) searchFloor(fp *PlaceFlags, y, x, probinc int) bool {
|
||||
if g.rnd(2+probinc) != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if found {
|
||||
g.look(false)
|
||||
g.Level.SetChar(y, x, Trap)
|
||||
|
||||
if !g.Options.Terse {
|
||||
g.addmsgf("you found ")
|
||||
}
|
||||
|
||||
if g.Player.On(Hallucinating) {
|
||||
g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)])
|
||||
} else {
|
||||
g.msg("%s", g.data.trName[*fp&FTrapMask])
|
||||
fp.Set(FSeen)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// help gives single character help, or the whole mess if he wants it
|
||||
@@ -585,6 +588,17 @@ func (g *RogueGame) help() {
|
||||
// If it's not a *, print the right help string or an error if he
|
||||
// typed a funny character.
|
||||
if helpch != '*' {
|
||||
g.helpOne(helpch)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
g.helpAll()
|
||||
}
|
||||
|
||||
// helpOne prints the help line for a single key, or an error for a
|
||||
// funny character (command.c help).
|
||||
func (g *RogueGame) helpOne(helpch byte) {
|
||||
g.move(0, 0)
|
||||
|
||||
for _, strp := range g.data.helpStr {
|
||||
@@ -598,27 +612,12 @@ func (g *RogueGame) help() {
|
||||
}
|
||||
|
||||
g.msg("unknown character '%s'", unctrl(helpch))
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
// Here we print help for everything, then wait before we return to
|
||||
// command mode.
|
||||
numprint := 0
|
||||
|
||||
for _, strp := range g.data.helpStr {
|
||||
if strp.Print {
|
||||
numprint++
|
||||
}
|
||||
}
|
||||
|
||||
if numprint&1 == 1 { // round odd numbers up
|
||||
numprint++
|
||||
}
|
||||
|
||||
numprint /= 2
|
||||
if numprint > NumLines-1 {
|
||||
numprint = NumLines - 1
|
||||
}
|
||||
// helpAll prints help for everything in two columns, then waits before
|
||||
// returning to command mode (command.c help).
|
||||
func (g *RogueGame) helpAll() {
|
||||
numprint := g.helpLines()
|
||||
|
||||
hw := g.scr.Hw
|
||||
hw.Clear()
|
||||
@@ -655,6 +654,29 @@ func (g *RogueGame) help() {
|
||||
g.refresh()
|
||||
}
|
||||
|
||||
// helpLines counts how many rows the two-column help listing needs
|
||||
// (command.c help).
|
||||
func (g *RogueGame) helpLines() int {
|
||||
numprint := 0
|
||||
|
||||
for _, strp := range g.data.helpStr {
|
||||
if strp.Print {
|
||||
numprint++
|
||||
}
|
||||
}
|
||||
|
||||
if numprint&1 == 1 { // round odd numbers up
|
||||
numprint++
|
||||
}
|
||||
|
||||
numprint /= 2
|
||||
if numprint > NumLines-1 {
|
||||
numprint = NumLines - 1
|
||||
}
|
||||
|
||||
return numprint
|
||||
}
|
||||
|
||||
// identify tells the player what a certain thing is (command.c identify).
|
||||
func (g *RogueGame) identify() {
|
||||
g.msg("what do you want identified? ")
|
||||
@@ -706,8 +728,18 @@ func (g *RogueGame) uLevel() {
|
||||
return
|
||||
}
|
||||
|
||||
if g.Level.Char(g.Player.Pos.Y, g.Player.Pos.X) == Stairs {
|
||||
if g.HasAmulet {
|
||||
if g.Level.Char(g.Player.Pos.Y, g.Player.Pos.X) != Stairs {
|
||||
g.msg("I see no way up")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !g.HasAmulet {
|
||||
g.msg("your way is magically blocked")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
g.Depth--
|
||||
if g.Depth == 0 {
|
||||
g.totalWinner()
|
||||
@@ -715,12 +747,6 @@ func (g *RogueGame) uLevel() {
|
||||
|
||||
g.NewLevel()
|
||||
g.msg("you feel a wrenching sensation in your gut")
|
||||
} else {
|
||||
g.msg("your way is magically blocked")
|
||||
}
|
||||
} else {
|
||||
g.msg("I see no way up")
|
||||
}
|
||||
}
|
||||
|
||||
// levitCheck checks whether she's levitating, and if she is, prints an
|
||||
@@ -744,63 +770,14 @@ func (g *RogueGame) call() {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
op *ObjInfo
|
||||
elsewise string
|
||||
know *bool
|
||||
guess *string
|
||||
)
|
||||
|
||||
it := &g.Items
|
||||
|
||||
switch obj.Kind {
|
||||
case KindRing:
|
||||
op = &it.Rings[obj.Which]
|
||||
elsewise = it.RingStones[obj.Which]
|
||||
case KindPotion:
|
||||
op = &it.Potions[obj.Which]
|
||||
elsewise = it.PotColors[obj.Which]
|
||||
case KindScroll:
|
||||
op = &it.Scrolls[obj.Which]
|
||||
elsewise = it.ScrNames[obj.Which]
|
||||
case KindWand:
|
||||
op = &it.Sticks[obj.Which]
|
||||
elsewise = it.WandMade[obj.Which]
|
||||
case KindFood:
|
||||
g.msg("you can't call that anything")
|
||||
|
||||
return
|
||||
default:
|
||||
guess = &obj.Label
|
||||
elsewise = obj.Label
|
||||
}
|
||||
|
||||
fromGuess := false
|
||||
|
||||
if op != nil {
|
||||
know = &op.Know
|
||||
|
||||
guess = &op.Guess
|
||||
if *guess != "" {
|
||||
elsewise = *guess
|
||||
fromGuess = true
|
||||
}
|
||||
} else {
|
||||
fromGuess = elsewise != "" && elsewise == *guess
|
||||
}
|
||||
|
||||
if know != nil && *know {
|
||||
g.msg("that has already been identified")
|
||||
|
||||
op, elsewise, guess, ok := g.callTarget(obj)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if fromGuess {
|
||||
if !g.Options.Terse {
|
||||
g.addmsgf("Was ")
|
||||
}
|
||||
|
||||
g.msg("called \"%s\"", elsewise)
|
||||
elsewise, guess, ok = g.callPrelude(op, elsewise, guess)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
g.msg("%s", g.chooseTerse("call it: ", "what do you want to call it? "))
|
||||
@@ -811,10 +788,82 @@ func (g *RogueGame) call() {
|
||||
}
|
||||
}
|
||||
|
||||
// callTarget picks what a call names: the lore record for identifiable
|
||||
// kinds, the object's own label otherwise; ok is false for food (the
|
||||
// switch of command.c call).
|
||||
func (g *RogueGame) callTarget(obj *Object) (*ObjInfo, string, *string, bool) {
|
||||
it := &g.Items
|
||||
|
||||
switch obj.Kind {
|
||||
case KindRing:
|
||||
return &it.Rings[obj.Which], it.RingStones[obj.Which], nil, true
|
||||
case KindPotion:
|
||||
return &it.Potions[obj.Which], it.PotColors[obj.Which], nil, true
|
||||
case KindScroll:
|
||||
return &it.Scrolls[obj.Which], it.ScrNames[obj.Which], nil, true
|
||||
case KindWand:
|
||||
return &it.Sticks[obj.Which], it.WandMade[obj.Which], nil, true
|
||||
case KindFood:
|
||||
g.msg("you can't call that anything")
|
||||
|
||||
return nil, "", nil, false
|
||||
default:
|
||||
return nil, obj.Label, &obj.Label, true
|
||||
}
|
||||
}
|
||||
|
||||
// callPrelude resolves the item's current name, reporting a previous
|
||||
// guess; ok is false when the item is already identified (command.c
|
||||
// call).
|
||||
func (g *RogueGame) callPrelude(op *ObjInfo, elsewise string, guess *string) (string, *string, bool) {
|
||||
fromGuess := false
|
||||
|
||||
if op != nil {
|
||||
if op.Know {
|
||||
g.msg("that has already been identified")
|
||||
|
||||
return "", nil, false
|
||||
}
|
||||
|
||||
guess = &op.Guess
|
||||
if *guess != "" {
|
||||
elsewise = *guess
|
||||
fromGuess = true
|
||||
}
|
||||
} else {
|
||||
fromGuess = elsewise != "" && elsewise == *guess
|
||||
}
|
||||
|
||||
if fromGuess {
|
||||
if !g.Options.Terse {
|
||||
g.addmsgf("Was ")
|
||||
}
|
||||
|
||||
g.msg("called \"%s\"", elsewise)
|
||||
}
|
||||
|
||||
return elsewise, guess, true
|
||||
}
|
||||
|
||||
// current prints the current weapon/armor (command.c current).
|
||||
func (g *RogueGame) current(cur *Object, how, where string) {
|
||||
g.After = false
|
||||
if cur != nil {
|
||||
if cur == nil {
|
||||
if !g.Options.Terse {
|
||||
g.addmsgf("you are ")
|
||||
}
|
||||
|
||||
g.addmsgf("%s nothing", how)
|
||||
|
||||
if where != "" {
|
||||
g.addmsgf(" %s", where)
|
||||
}
|
||||
|
||||
g.endmsg()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if !g.Options.Terse {
|
||||
g.addmsgf("you are %s (", how)
|
||||
}
|
||||
@@ -828,19 +877,6 @@ func (g *RogueGame) current(cur *Object, how, where string) {
|
||||
}
|
||||
|
||||
g.endmsg()
|
||||
} else {
|
||||
if !g.Options.Terse {
|
||||
g.addmsgf("you are ")
|
||||
}
|
||||
|
||||
g.addmsgf("%s nothing", how)
|
||||
|
||||
if where != "" {
|
||||
g.addmsgf(" %s", where)
|
||||
}
|
||||
|
||||
g.endmsg()
|
||||
}
|
||||
}
|
||||
|
||||
// shell lets them escape for a while (main.c shell). The terminal decides
|
||||
|
||||
139
game/tables.go
139
game/tables.go
@@ -142,6 +142,11 @@ type gameData struct {
|
||||
// lands, indexed by monster letter - 'A': the cases of the fight.c
|
||||
// attack switch. A true return means the monster removed itself.
|
||||
hitHandlers [26]func(g *RogueGame, mp *Monster, mname string) bool
|
||||
|
||||
// commandHandlers dispatches the ordinary command keys: the simple
|
||||
// cases of the big command.c switch. Keys that re-dispatch (runs,
|
||||
// fight, repeat, move-on) and wizard keys stay in dispatchKey.
|
||||
commandHandlers map[byte]func(g *RogueGame)
|
||||
}
|
||||
|
||||
// ripWall is the repeated blank wall line of the tombstone art.
|
||||
@@ -672,6 +677,140 @@ func newGameData() *gameData {
|
||||
'V' - 'A': (*RogueGame).hitLifeDrainer,
|
||||
'W' - 'A': (*RogueGame).hitLifeDrainer,
|
||||
},
|
||||
|
||||
commandHandlers: map[byte]func(*RogueGame){
|
||||
',': (*RogueGame).pickupCommand,
|
||||
'!': (*RogueGame).shell,
|
||||
'h': func(g *RogueGame) { g.moveHero(0, -1) },
|
||||
'j': func(g *RogueGame) { g.moveHero(1, 0) },
|
||||
'k': func(g *RogueGame) { g.moveHero(-1, 0) },
|
||||
'l': func(g *RogueGame) { g.moveHero(0, 1) },
|
||||
'y': func(g *RogueGame) { g.moveHero(-1, -1) },
|
||||
'u': func(g *RogueGame) { g.moveHero(-1, 1) },
|
||||
'b': func(g *RogueGame) { g.moveHero(1, -1) },
|
||||
'n': func(g *RogueGame) { g.moveHero(1, 1) },
|
||||
'H': func(g *RogueGame) { g.startRun('h') },
|
||||
'J': func(g *RogueGame) { g.startRun('j') },
|
||||
'K': func(g *RogueGame) { g.startRun('k') },
|
||||
'L': func(g *RogueGame) { g.startRun('l') },
|
||||
'Y': func(g *RogueGame) { g.startRun('y') },
|
||||
'U': func(g *RogueGame) { g.startRun('u') },
|
||||
'B': func(g *RogueGame) { g.startRun('b') },
|
||||
'N': func(g *RogueGame) { g.startRun('n') },
|
||||
't': func(g *RogueGame) {
|
||||
if !g.promptDirection() {
|
||||
g.After = false
|
||||
} else {
|
||||
g.missile(g.Delta.Y, g.Delta.X)
|
||||
}
|
||||
},
|
||||
'q': (*RogueGame).quaff,
|
||||
'Q': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.QComm = true
|
||||
g.quit(0)
|
||||
g.QComm = false
|
||||
},
|
||||
'i': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.inventory(g.Player.Pack, 0)
|
||||
},
|
||||
'I': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.pickyInven()
|
||||
},
|
||||
'd': (*RogueGame).dropIt,
|
||||
'r': (*RogueGame).readScroll,
|
||||
'e': (*RogueGame).eat,
|
||||
'w': (*RogueGame).wield,
|
||||
'W': (*RogueGame).wear,
|
||||
'T': (*RogueGame).takeOff,
|
||||
'P': (*RogueGame).ringOn,
|
||||
'R': (*RogueGame).ringOff,
|
||||
'o': func(g *RogueGame) {
|
||||
g.option()
|
||||
g.After = false
|
||||
},
|
||||
'c': func(g *RogueGame) {
|
||||
g.call()
|
||||
g.After = false
|
||||
},
|
||||
'>': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.dLevel()
|
||||
},
|
||||
'<': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.uLevel()
|
||||
},
|
||||
'?': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.help()
|
||||
},
|
||||
'/': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.identify()
|
||||
},
|
||||
's': (*RogueGame).search,
|
||||
'z': func(g *RogueGame) {
|
||||
if g.promptDirection() {
|
||||
g.doZap()
|
||||
} else {
|
||||
g.After = false
|
||||
}
|
||||
},
|
||||
'D': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.discovered()
|
||||
},
|
||||
CTRL('P'): func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.msg("%s", g.Msgs.Huh)
|
||||
},
|
||||
CTRL('R'): func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.refresh()
|
||||
},
|
||||
'v': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.msg("version %s. (mctesq was here)", Release)
|
||||
},
|
||||
'S': func(g *RogueGame) {
|
||||
g.After = false
|
||||
g.saveGame()
|
||||
},
|
||||
'.': func(*RogueGame) {
|
||||
// rest command
|
||||
},
|
||||
' ': func(g *RogueGame) {
|
||||
g.After = false // "legal" illegal command
|
||||
},
|
||||
'^': (*RogueGame).identifyTrapCommand,
|
||||
Escape: func(g *RogueGame) {
|
||||
g.DoorStop = false
|
||||
g.Count = 0
|
||||
g.After = false
|
||||
g.Again = false
|
||||
},
|
||||
')': func(g *RogueGame) {
|
||||
g.current(g.Player.CurWeapon, "wielding", "")
|
||||
},
|
||||
']': func(g *RogueGame) {
|
||||
g.current(g.Player.CurArmor, "wearing", "")
|
||||
},
|
||||
'=': func(g *RogueGame) {
|
||||
g.current(g.Player.CurRing[Left], "wearing",
|
||||
g.chooseTerse("(L)", "on left hand"))
|
||||
g.current(g.Player.CurRing[Right], "wearing",
|
||||
g.chooseTerse("(R)", "on right hand"))
|
||||
},
|
||||
'@': func(g *RogueGame) {
|
||||
g.StatMsg = true
|
||||
g.status()
|
||||
g.StatMsg = false
|
||||
g.After = false
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user