package game // misc.c — look() display maintenance, direction input, eating, level-ups, // and small utilities. call_it arrives with the scroll/potion phase (it // needs the get_str line editor). // 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 { g.eraseLamp(g.Oldpos, g.Oldrp) g.Oldpos = hero g.Oldrp = rp } ey := hero.Y + 1 ex := hero.X + 1 sx := hero.X - 1 sy := hero.Y - 1 sumhero, diffhero := 0, 0 if g.DoorStop && !g.Firstmove && g.Running { sumhero = hero.Y + hero.X diffhero = hero.Y - hero.X } pp := g.Level.At(hero.Y, hero.X) pch := pp.Ch pfl := pp.Flags for y := sy; y <= ey; y++ { if y <= 0 || y >= NumLines-1 { continue } 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 if tp == nil { ch = g.tripCh(y, x, ch) } else if p.On(SenseMonsters) && tp.On(Invisible) { if g.DoorStop && !g.Firstmove { g.Running = false } continue } else { if wakeup { g.wakeMonster(y, x) } if g.seeMonst(tp) { if p.On(Hallucinating) { ch = byte(g.rnd(26) + 'A') } 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 { g.Running = false } if !g.Running || !g.Options.Jump { g.mvaddch(hero.Y, hero.X, PlayerCh) } } // 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 { if g.Player.On(Hallucinating) && g.After { switch ch { case Floor, ' ', Passage, '-', '|', Door, Trap: default: if y != g.Level.Stairs.Y || x != g.Level.Stairs.X || !g.SeenStairs { ch = g.rndThing() } } } return ch } // eraseLamp erases the area shown by a lamp in a dark room // (misc.c erase_lamp). func (g *RogueGame) eraseLamp(pos Coord, rp *Room) { if !(g.Options.SeeFloor && rp.Flags&(Gone|Dark) == Dark && !g.Player.On(Blind)) { return } ey := pos.Y + 1 ex := pos.X + 1 sy := pos.Y - 1 for x := pos.X - 1; x <= ex; x++ { for y := sy; y <= ey; y++ { if y == g.Player.Pos.Y && x == g.Player.Pos.X { continue } g.move(y, x) if g.inch() == Floor { g.addch(' ') } } } } // showFloor reports whether we show the floor in her room at this time // (misc.c show_floor). func (g *RogueGame) showFloor() bool { if g.Player.Room.Flags&(Gone|Dark) == Dark && !g.Player.On(Blind) { return g.Options.SeeFloor } 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). func (g *RogueGame) eat() { obj := g.getItem("eat", KindFood) if obj == nil { return } if obj.Kind != KindFood { if !g.Options.Terse { g.msg("ugh, you would get ill if you ate that") } else { g.msg("that's Inedible!") } return } p := &g.Player if p.FoodLeft < 0 { p.FoodLeft = 0 } if p.FoodLeft += HungerTime - 200 + g.rnd(400); p.FoodLeft > StomachSize { p.FoodLeft = StomachSize } p.HungryState = 0 if obj == p.CurWeapon { p.CurWeapon = nil } if obj.Which == 1 { g.msg("my, that was a yummy %s", g.Fruit) } else if g.rnd(100) > 70 { p.Stats.Exp++ g.msg("%s, this food tastes awful", g.chooseStr("bummer", "yuk")) g.checkLevel() } else { g.msg("%s, that tasted good", g.chooseStr("oh, wow", "yum")) } g.leavePack(obj, false, false) } // checkLevel checks to see if the guy has gone up a level (misc.c // check_level). func (g *RogueGame) checkLevel() { p := &g.Player var i int for i = 0; eLevels[i] != 0; i++ { if eLevels[i] > p.Stats.Exp { break } } i++ olevel := p.Stats.Lvl p.Stats.Lvl = i if i > olevel { add := g.roll(i-olevel, 10) p.Stats.MaxHP += add p.Stats.HP += add g.msg("welcome to level %d", i) } } // chgStr modifies the player's strength, keeping track of the highest it // has been (misc.c chg_str). func (g *RogueGame) chgStr(amt int) { if amt == 0 { return } p := &g.Player addStr(&p.Stats.Str, amt) comp := p.Stats.Str if p.IsRing(Left, RingAddStrength) { addStr(&comp, -p.CurRing[Left].Arm) } if p.IsRing(Right, RingAddStrength) { addStr(&comp, -p.CurRing[Right].Arm) } if comp > p.MaxStats.Str { p.MaxStats.Str = comp } } // addStr performs the actual strength add, checking bounds (misc.c add_str). func addStr(sp *int, amt int) { if *sp += amt; *sp < 3 { *sp = 3 } else if *sp > 31 { *sp = 31 } } // addHaste adds a haste to the player (misc.c add_haste). func (g *RogueGame) addHaste(potion bool) bool { p := &g.Player if p.On(Hasted) { g.NoCommand += g.rnd(8) p.Flags.Clear(Awake | Hasted) g.Extinguish(DNohaste) g.msg("you faint from exhaustion") return false } p.Flags.Set(Hasted) if potion { g.Fuse(DNohaste, 0, g.rnd(4)+4, After) } return true } // aggravate aggravates all the monsters on this level (misc.c aggravate). func (g *RogueGame) aggravate() { // runto() can splice the monster list while we walk it, so iterate a copy. monsters := append([]*Monster(nil), g.Level.Monsters...) for _, mp := range monsters { g.runto(mp.Pos) } } // vowelstr returns "n" if the string starts with a vowel, for "a"/"an" // (misc.c vowelstr). func vowelstr(str string) string { if str == "" { return "" } switch str[0] { case 'a', 'A', 'e', 'E', 'i', 'I', 'o', 'O', 'u', 'U': return "n" } return "" } // isCurrent sees if the object is one of the currently used items // (misc.c is_current). func (g *RogueGame) isCurrent(obj *Object) bool { if obj == nil { return false } p := &g.Player if obj == p.CurArmor || obj == p.CurWeapon || obj == p.CurRing[Left] || obj == p.CurRing[Right] { if !g.Options.Terse { g.addmsg("That's already ") } g.msg("in use") return true } return false } // getDir sets up the direction coordinate for use in various "prefix" // commands (misc.c get_dir). func (g *RogueGame) getDir() bool { if g.Again && g.LastDir != 0 { g.Delta = g.lastDelt g.DirCh = g.LastDir } else { prompt := "direction: " if !g.Options.Terse { prompt = "which direction? " g.msg("%s", prompt) } 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.LastDir = 0 g.resetLast() return false default: g.Msgs.Mpos = 0 g.msg("%s", prompt) gotit = false } if gotit { break } } g.DirCh = toLower(g.DirCh) g.LastDir = g.DirCh g.lastDelt = g.Delta } 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.Msgs.Mpos = 0 return true } // callIt calls an object something after use (misc.c call_it). func (g *RogueGame) callIt(info *ObjInfo) { if info.Know { info.Guess = "" } else if info.Guess == "" { g.msg("%s", g.chooseTerse("call it: ", "what do you want to call it? ")) buf := "" if g.getStr(&buf, g.scr.Std) == Norm { if buf != "" { info.Guess = buf } } } } // thingList is misc.c rnd_thing()'s static table. var thingList = []byte{ Potion, Scroll, Ring, Stick, Food, Weapon, Armor, Stairs, Gold, Amulet, } // rndThing picks a random thing appropriate for this level (misc.c // rnd_thing). func (g *RogueGame) rndThing() byte { var i int if g.Depth >= AmuletLevel { i = g.rnd(len(thingList)) } else { i = g.rnd(len(thingList) - 1) } return thingList[i] } // chooseStr picks the first or second string depending on whether the // player is tripping (misc.c choose_str). func (g *RogueGame) chooseStr(ts, ns string) string { if g.Player.On(Hallucinating) { return ts } return ns } // unctrl gives a printable representation of a character, like curses // unctrl(): control characters display as ^X. func unctrl(ch byte) string { if ch < ' ' { return "^" + string(ch+'@') } if ch == 0x7f { return "^?" } return string(ch) }