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.
897 lines
17 KiB
Go
897 lines
17 KiB
Go
package game
|
|
|
|
// command.c — read and execute the user commands.
|
|
|
|
// command processes one player command, with its BEFORE/AFTER daemon
|
|
// bracketing (command.c command).
|
|
func (g *RogueGame) command() {
|
|
p := &g.Player
|
|
|
|
ntimes := 1 // number of player moves
|
|
if p.On(Hasted) {
|
|
ntimes++
|
|
}
|
|
// Let the daemons start up
|
|
g.DoDaemons(Before)
|
|
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()
|
|
g.HasHit = false
|
|
}
|
|
// these are illegal things for the player to be, so if any are
|
|
// set, someone's been poking in memory
|
|
if p.On(Slowed | Greedy | Invisible | Regenerates | Targeted) {
|
|
panic("player flags corrupted")
|
|
}
|
|
|
|
g.look(true)
|
|
|
|
if !g.Running {
|
|
g.DoorStop = false
|
|
}
|
|
|
|
g.status()
|
|
g.LastScore = p.Purse
|
|
g.move(p.Pos.Y, p.Pos.X)
|
|
|
|
if (!g.Running && g.Count == 0) || !g.Options.Jump {
|
|
g.refresh() // draw screen
|
|
}
|
|
|
|
g.Take = 0
|
|
g.After = true
|
|
// Read command or continue run
|
|
if g.Wizard {
|
|
g.NoScore = true
|
|
}
|
|
}
|
|
|
|
// 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:
|
|
return g.RunCh
|
|
case g.Count != 0:
|
|
return g.countCh
|
|
default:
|
|
ch := g.readchar()
|
|
|
|
g.MoveOn = false
|
|
if g.Msgs.Mpos != 0 { // erase message if its there
|
|
g.msg("")
|
|
}
|
|
|
|
return ch
|
|
}
|
|
}
|
|
|
|
// 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
|
|
ch = g.countPrefix(ch)
|
|
// execute a command
|
|
if g.Count != 0 && !g.Running {
|
|
g.Count--
|
|
}
|
|
|
|
if ch != 'a' && ch != Escape &&
|
|
!g.Running && g.Count == 0 && !g.ToDeath {
|
|
g.LLastComm = g.LastComm
|
|
g.LLastDir = g.LastDir
|
|
g.LLastPick = g.LastPick
|
|
g.LastComm = ch
|
|
g.LastDir = 0
|
|
g.LastPick = nil
|
|
}
|
|
|
|
g.dispatch(ch)
|
|
// turn off flags if no longer needed
|
|
if !g.Running {
|
|
g.DoorStop = false
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
return ch
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
ch = newCh
|
|
}
|
|
}
|
|
|
|
// 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
|
|
|
|
var found *Object
|
|
|
|
for _, obj := range g.Level.Objects {
|
|
if obj.Pos.Y == p.Pos.Y && obj.Pos.X == p.Pos.X {
|
|
found = obj
|
|
|
|
break
|
|
}
|
|
}
|
|
|
|
if found != nil {
|
|
if !g.levitCheck() {
|
|
g.pickUp(found.Kind.Glyph())
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if !g.Options.Terse {
|
|
g.addmsgf("there is ")
|
|
}
|
|
|
|
g.addmsgf("nothing here")
|
|
|
|
if !g.Options.Terse {
|
|
g.addmsgf(" to pick up")
|
|
}
|
|
|
|
g.endmsg()
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
if !g.promptDirection() {
|
|
g.After = false
|
|
|
|
return 0, false
|
|
}
|
|
|
|
g.Delta.Y += p.Pos.Y
|
|
g.Delta.X += p.Pos.X
|
|
|
|
mp := g.Level.MonsterAt(g.Delta.Y, g.Delta.X)
|
|
if mp == nil || (!g.seeMonst(mp) && !p.On(SenseMonsters)) {
|
|
if !g.Options.Terse {
|
|
g.addmsgf("I see ")
|
|
}
|
|
|
|
g.msg("no monster there")
|
|
g.After = false
|
|
} else if g.diagOk(p.Pos, g.Delta) {
|
|
g.ToDeath = true
|
|
g.MaxHit = 0
|
|
|
|
mp.Flags.Set(Targeted)
|
|
|
|
g.RunCh = g.DirCh
|
|
|
|
return g.DirCh, true // the C goto over: fight by running at it
|
|
}
|
|
|
|
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() {
|
|
return
|
|
}
|
|
|
|
g.Delta.Y += p.Pos.Y
|
|
g.Delta.X += p.Pos.X
|
|
|
|
fp := g.Level.FlagsAt(g.Delta.Y, g.Delta.X)
|
|
if !g.Options.Terse {
|
|
g.addmsgf("You have found ")
|
|
}
|
|
|
|
switch {
|
|
case g.Level.Char(g.Delta.Y, g.Delta.X) != Trap:
|
|
g.msg("no trap there")
|
|
case p.On(Hallucinating):
|
|
g.msg("%s", g.data.trName[g.rnd(NumTrapTypes)])
|
|
default:
|
|
g.msg("%s", g.data.trName[*fp&FTrapMask])
|
|
fp.Set(FSeen)
|
|
}
|
|
}
|
|
|
|
// wizardCommand handles the MASTER debug commands (command.c).
|
|
func (g *RogueGame) wizardCommand(ch byte) {
|
|
p := &g.Player
|
|
|
|
switch ch {
|
|
case '|':
|
|
g.msg("@ %d,%d", p.Pos.Y, p.Pos.X)
|
|
case 'C':
|
|
g.createObj()
|
|
case '$':
|
|
g.msg("inpack = %d", p.Inpack)
|
|
case CTRL('G'):
|
|
g.inventory(g.Level.Objects, 0)
|
|
case CTRL('W'):
|
|
g.whatis(false, 0)
|
|
case CTRL('D'):
|
|
g.Depth++
|
|
g.NewLevel()
|
|
case CTRL('A'):
|
|
g.Depth--
|
|
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'):
|
|
g.msg("food left: %d", p.FoodLeft)
|
|
case CTRL('C'):
|
|
g.addPass()
|
|
case CTRL('X'):
|
|
g.turnSee(p.On(SenseMonsters))
|
|
case CTRL('~'):
|
|
if item, ok := g.promptPackItem("charge", KindWand); ok {
|
|
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()
|
|
}
|
|
// Give him a sword (+1,+1)
|
|
obj := newObject()
|
|
g.initWeapon(obj, WeaponTwoHandedSword)
|
|
obj.HPlus = 1
|
|
obj.DPlus = 1
|
|
g.addPack(obj, true)
|
|
p.CurWeapon = obj
|
|
// And his suit of armor
|
|
obj = newObject()
|
|
obj.Kind = KindArmor
|
|
obj.Which = int(ArmorPlateMail)
|
|
obj.ArmorClass = -5
|
|
obj.Flags.Set(Known)
|
|
obj.Count = 1
|
|
p.CurArmor = obj
|
|
g.addPack(obj, true)
|
|
}
|
|
|
|
// illcom reports an illegal command (command.c illcom).
|
|
func (g *RogueGame) illcom(ch byte) {
|
|
g.Msgs.SaveMsg = false
|
|
g.Count = 0
|
|
g.msg("illegal command '%s'", unctrl(ch))
|
|
g.Msgs.SaveMsg = true
|
|
}
|
|
|
|
// search gropes about to find hidden things (command.c search).
|
|
func (g *RogueGame) search() {
|
|
p := &g.Player
|
|
ey := p.Pos.Y + 1
|
|
ex := p.Pos.X + 1
|
|
|
|
probinc := 0
|
|
if p.On(Hallucinating) {
|
|
probinc = 3
|
|
}
|
|
|
|
if p.On(Blind) {
|
|
probinc += 2
|
|
}
|
|
|
|
found := false
|
|
|
|
for y := p.Pos.Y - 1; y <= ey; y++ {
|
|
for x := p.Pos.X - 1; x <= ex; x++ {
|
|
if y == p.Pos.Y && x == p.Pos.X {
|
|
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) {
|
|
return false
|
|
}
|
|
|
|
foundone := false
|
|
|
|
switch g.Level.Char(y, x) {
|
|
case '|', '-':
|
|
if g.rnd(5+probinc) != 0 {
|
|
break
|
|
}
|
|
|
|
g.Level.SetChar(y, x, Door)
|
|
g.msg("a secret door")
|
|
|
|
foundone = true
|
|
case Floor:
|
|
foundone = g.searchFloor(fp, y, x, probinc)
|
|
case ' ':
|
|
if g.rnd(3+probinc) != 0 {
|
|
break
|
|
}
|
|
|
|
g.Level.SetChar(y, x, Passage)
|
|
|
|
foundone = true
|
|
}
|
|
|
|
if foundone {
|
|
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
|
|
}
|
|
|
|
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
|
|
// (command.c help).
|
|
func (g *RogueGame) help() {
|
|
g.msg("character you want help for (* for all): ")
|
|
helpch := g.readchar()
|
|
g.Msgs.Mpos = 0
|
|
// 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 {
|
|
if strp.Ch == helpch {
|
|
g.Msgs.LowerMsg = true
|
|
g.msg("%s%s", unctrl(strp.Ch), strp.Desc)
|
|
g.Msgs.LowerMsg = false
|
|
|
|
return
|
|
}
|
|
}
|
|
|
|
g.msg("unknown character '%s'", unctrl(helpch))
|
|
}
|
|
|
|
// 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()
|
|
|
|
cnt := 0
|
|
|
|
for _, strp := range g.data.helpStr {
|
|
if !strp.Print {
|
|
continue
|
|
}
|
|
|
|
x := 0
|
|
if cnt >= numprint {
|
|
x = NumCols / 2
|
|
}
|
|
|
|
hw.Move(cnt%numprint, x)
|
|
|
|
if strp.Ch != 0 {
|
|
hw.AddStr(unctrl(strp.Ch))
|
|
}
|
|
|
|
hw.AddStr(strp.Desc)
|
|
|
|
if cnt++; cnt >= numprint*2 {
|
|
break
|
|
}
|
|
}
|
|
|
|
hw.MvAddStr(NumLines-1, 0, "--Press space to continue--")
|
|
g.scr.RefreshWin(hw)
|
|
g.waitFor(' ')
|
|
g.msg("")
|
|
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? ")
|
|
ch := g.readchar()
|
|
|
|
g.Msgs.Mpos = 0
|
|
if ch == Escape {
|
|
g.msg("")
|
|
|
|
return
|
|
}
|
|
|
|
var str string
|
|
if isUpper(ch) {
|
|
str = g.Monsters[ch-'A'].Name
|
|
} else {
|
|
str = "unknown character"
|
|
|
|
for _, hp := range g.data.identList {
|
|
if hp.Ch == ch {
|
|
str = hp.Desc
|
|
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
g.msg("'%s': %s", unctrl(ch), str)
|
|
}
|
|
|
|
// dLevel: he wants to go down a level (command.c d_level).
|
|
func (g *RogueGame) dLevel() {
|
|
if g.levitCheck() {
|
|
return
|
|
}
|
|
|
|
if g.Level.Char(g.Player.Pos.Y, g.Player.Pos.X) != Stairs {
|
|
g.msg("I see no way down")
|
|
} else {
|
|
g.Depth++
|
|
g.SeenStairs = false
|
|
g.NewLevel()
|
|
}
|
|
}
|
|
|
|
// uLevel: he wants to go up a level (command.c u_level).
|
|
func (g *RogueGame) uLevel() {
|
|
if g.levitCheck() {
|
|
return
|
|
}
|
|
|
|
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()
|
|
}
|
|
|
|
g.NewLevel()
|
|
g.msg("you feel a wrenching sensation in your gut")
|
|
}
|
|
|
|
// levitCheck checks whether she's levitating, and if she is, prints an
|
|
// appropriate message (command.c levit_check).
|
|
func (g *RogueGame) levitCheck() bool {
|
|
if !g.Player.On(Levitating) {
|
|
return false
|
|
}
|
|
|
|
g.msg("You can't. You're floating off the ground!")
|
|
|
|
return true
|
|
}
|
|
|
|
// call allows a user to call a potion, scroll, or ring something
|
|
// (command.c call).
|
|
func (g *RogueGame) call() {
|
|
obj, ok := g.promptPackItem("call", KindCallable)
|
|
// Make certain that it is something that we want to name
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
op, elsewise, guess, ok := g.callTarget(obj)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
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? "))
|
|
|
|
buf := elsewise
|
|
if g.getStr(&buf, g.scr.Std) == Norm {
|
|
*guess = buf
|
|
}
|
|
}
|
|
|
|
// 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 !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)
|
|
}
|
|
|
|
g.InvDescribe = false
|
|
g.addmsgf("%c) %s", cur.PackCh, g.inventoryName(cur, true))
|
|
|
|
g.InvDescribe = true
|
|
if where != "" {
|
|
g.addmsgf(" %s", where)
|
|
}
|
|
|
|
g.endmsg()
|
|
}
|
|
|
|
// shell lets them escape for a while (main.c shell). The terminal decides
|
|
// how to actually suspend; headless terminals just decline.
|
|
func (g *RogueGame) shell() {
|
|
g.After = false
|
|
if se, ok := g.scr.term.(interface{ ShellEscape() }); ok {
|
|
g.InShell = true
|
|
|
|
se.ShellEscape()
|
|
|
|
g.InShell = false
|
|
g.refresh()
|
|
} else {
|
|
g.msg("shell escape is not available")
|
|
}
|
|
}
|