package game // move.c — hero movement commands. // doRun starts the hero running (move.c do_run). func (g *RogueGame) doRun(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) { p := &g.Player g.Firstmove = false if g.NoMove > 0 { g.NoMove-- g.msg("you are still stuck in the bear trap") return } // Do a confused move (maybe) var nh Coord if p.On(IsHuh) && g.rnd(5) != 0 { nh = g.rndmove(&p.Creature) if nh == p.Pos { g.After = false g.Running = false g.ToDeath = false return } } else { 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 var ch byte var fl PlaceFlags 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(IsLevit) { ch = Trap g.Level.SetChar(nh.Y, nh.X, Trap) g.Level.FlagsAt(nh.Y, nh.X).Set(FReal) } } else if p.On(IsHeld) && ch != 'F' { g.msg("you are being held") return } } if hitBound { ch = ' ' // fall into the wall case below } switch ch { case ' ', '|', '-': if g.Options.PassGo && g.Running && p.Room.Flags.Has(IsGone) && !p.On(IsBlind) { 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 } 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 } dy = 0 g.turnref() nh = Coord{Y: p.Pos.Y + dy, X: p.Pos.X + dx} goto over } } } g.Running = false g.After = false case Door: g.Running = false if g.Level.FlagsAt(p.Pos.Y, p.Pos.X).Has(FPass) { g.enterRoom(nh) } g.moveStuff(nh, fl) case Trap: tr := g.beTrapped(nh) if tr == TDoor || tr == TTelep { 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) } } } // moveStuff is the move_stuff label in do_move: complete the step. func (g *RogueGame) moveStuff(nh Coord, fl PlaceFlags) { p := &g.Player g.mvaddch(p.Pos.Y, p.Pos.X, g.floorAt()) if fl.Has(FPass) && g.Level.Char(g.Oldpos.Y, g.Oldpos.X) == Door { g.leaveRoom(nh) } p.Pos = nh } // turnOk decides whether it is legal to turn onto the given space // (move.c turn_ok). func (g *RogueGame) turnOk(y, x int) bool { pp := g.Level.At(y, x) return pp.Ch == Door || pp.Flags&(FReal|FPass) == (FReal|FPass) } // turnref decides whether to refresh at a passage turning (move.c turnref). func (g *RogueGame) turnref() { p := &g.Player pp := g.Level.At(p.Pos.Y, p.Pos.X) if !pp.Flags.Has(FSeen) { if g.Options.Jump { g.refresh() } pp.Flags.Set(FSeen) } } // doorOpen is called to wake up things in a room that might move when the // hero enters (move.c door_open). func (g *RogueGame) doorOpen(rp *Room) { if rp.Flags.Has(IsGone) { return } for y := rp.Pos.Y; y < rp.Pos.Y+rp.Max.Y; y++ { for x := rp.Pos.X; x < rp.Pos.X+rp.Max.X; x++ { if isUpper(g.Level.VisibleChar(y, x)) { g.wakeMonster(y, x) } } } } // beTrapped makes him pay for stepping on a trap (move.c be_trapped). func (g *RogueGame) beTrapped(tc Coord) int { p := &g.Player if p.On(IsLevit) { return TRust // anything that's not a door or teleport } g.Running = false g.Count = 0 pp := g.Level.At(tc.Y, tc.X) pp.Ch = Trap tr := int(pp.Flags & FTMask) pp.Flags.Set(FSeen) switch tr { case TDoor: g.Depth++ g.NewLevel() g.msg("you fell into a trap!") case TBear: g.NoMove += g.spread(3) // BEARTIME g.msg("you are caught in a bear trap") case TMyst: switch g.rnd(11) { case 0: g.msg("you are suddenly in a parallel dimension") case 1: g.msg("the light in here suddenly seems %s", rainbow[g.rnd(len(rainbow))]) case 2: g.msg("you feel a sting in the side of your neck") case 3: g.msg("multi-colored lines swirl around you, then fade") case 4: g.msg("a %s light flashes in your eyes", rainbow[g.rnd(len(rainbow))]) case 5: g.msg("a spike shoots past your ear!") case 6: g.msg("%s sparks dance across your armor", rainbow[g.rnd(len(rainbow))]) case 7: g.msg("you suddenly feel very thirsty") case 8: g.msg("you feel time speed up suddenly") case 9: g.msg("time now seems to be going slower") case 10: g.msg("you pack turns %s!", rainbow[g.rnd(len(rainbow))]) } case TSleep: g.NoCommand += g.spread(5) // SLEEPTIME p.Flags.Clear(IsRun) g.msg("a strange white mist envelops you and you fall asleep") case TArrow: if g.swing(p.Stats.Lvl-1, p.Stats.Arm, 1) { p.Stats.HP -= g.roll(1, 6) if p.Stats.HP <= 0 { g.msg("an arrow killed you") g.death('a') } else { g.msg("oh no! An arrow shot you") } } else { arrow := newObject() g.initWeapon(arrow, Arrow) arrow.Count = 1 arrow.Pos = p.Pos g.fall(arrow, false) g.msg("an arrow shoots past you") } case TTelep: // since the hero's leaving, look() won't put a TRAP down for us, // so we have to do it ourself g.teleport() g.mvaddch(tc.Y, tc.X, Trap) case TDart: if !g.swing(p.Stats.Lvl+1, p.Stats.Arm, 1) { g.msg("a small dart whizzes by your ear and vanishes") } else { p.Stats.HP -= g.roll(1, 4) if p.Stats.HP <= 0 { g.msg("a poisoned dart killed you") g.death('d') } if !p.IsWearing(RSustStr) && !g.save(VsPoison) { g.chgStr(-1) } g.msg("a small dart just hit you in the shoulder") } case TRust: g.msg("a gush of water hits you on the head") g.rustArmor(p.CurArmor) } g.flushType() return tr } // rndmove moves in a random direction if the monster/person is confused // (move.c rndmove). func (g *RogueGame) rndmove(who *Creature) Coord { ret := Coord{ Y: who.Pos.Y + g.rnd(3) - 1, X: who.Pos.X + g.rnd(3) - 1, } // Now check to see if that's a legal move. If not, don't move. // (I.e., bump into the wall or whatever) if ret == who.Pos { return ret } if !g.diagOk(who.Pos, ret) { return who.Pos } ch := g.Level.VisibleChar(ret.Y, ret.X) if !stepOk(ch) { return who.Pos } if ch == Scroll { var found *Object for _, obj := range g.Level.Objects { if ret.Y == obj.Pos.Y && ret.X == obj.Pos.X { found = obj break } } if found != nil && found.Which == SScare { return who.Pos } } return ret } // rustArmor rusts the given armor, if it is a legal kind to rust, and we // aren't wearing a magic ring (move.c rust_armor). func (g *RogueGame) rustArmor(arm *Object) { if arm == nil || arm.Type != Armor || arm.Which == Leather || arm.Arm >= 9 { return } if arm.Flags.Has(IsProt) || g.Player.IsWearing(RSustArm) { if !g.ToDeath { g.msg("the rust vanishes instantly") } } else { arm.Arm++ if !g.Options.Terse { g.msg("your armor appears to be weaker now. Oh my!") } else { g.msg("your armor weakens") } } }