package game // chase.c — the navigation and visibility half. The chase driver (runners, // move_monst, do_chase, chase) arrives with the combat phase, since it // attacks the player and fires dragon bolts. // setOldch sets the oldch character for the monster (chase.c set_oldch). func (g *RogueGame) setOldch(tp *Monster, cp Coord) { if tp.Pos == cp { return } sch := tp.OldCh tp.OldCh = g.mvinch(cp.Y, cp.X) if !g.Player.On(IsBlind) { if (sch == Floor || tp.OldCh == Floor) && tp.Room.Flags.Has(IsDark) { tp.OldCh = ' ' } else if distCp(cp, g.Player.Pos) <= LampDist && g.Options.SeeFloor { tp.OldCh = g.Level.Char(cp.Y, cp.X) } } } // seeMonst returns true if the hero can see the monster (chase.c // see_monst). func (g *RogueGame) seeMonst(mp *Monster) bool { p := &g.Player if p.On(IsBlind) { return false } if mp.On(IsInvis) && !p.On(CanSee) { return false } y, x := mp.Pos.Y, mp.Pos.X if distance(y, x, p.Pos.Y, p.Pos.X) < LampDist { if y != p.Pos.Y && x != p.Pos.X && !stepOk(g.Level.Char(y, p.Pos.X)) && !stepOk(g.Level.Char(p.Pos.Y, x)) { return false } return true } if mp.Room != p.Room { return false } return !mp.Room.Flags.Has(IsDark) } // runto sets a monster running after the hero (chase.c runto). func (g *RogueGame) runto(runner Coord) { tp := g.Level.MonsterAt(runner.Y, runner.X) if tp == nil { return } // Start the beastie running tp.Flags.Set(IsRun) tp.Flags.Clear(IsHeld) tp.Dest = g.findDest(tp) } // roomin finds what room some coordinates are in; nil means they aren't in // any room (chase.c roomin). func (g *RogueGame) roomin(cp Coord) *Room { fp := *g.Level.FlagsAt(cp.Y, cp.X) if fp.Has(FPass) { return &g.Level.Passages[fp&FPNum] } for i := range g.Level.Rooms { rp := &g.Level.Rooms[i] if cp.X <= rp.Pos.X+rp.Max.X && rp.Pos.X <= cp.X && cp.Y <= rp.Pos.Y+rp.Max.Y && rp.Pos.Y <= cp.Y { return rp } } g.msg("in some bizarre place (%d, %d)", cp.Y, cp.X) return nil } // diagOk checks to see if a diagonal move is legal (chase.c diag_ok). func (g *RogueGame) diagOk(sp, ep Coord) bool { if ep.X < 0 || ep.X >= NumCols || ep.Y <= 0 || ep.Y >= NumLines-1 { return false } if ep.X == sp.X || ep.Y == sp.Y { return true } return stepOk(g.Level.Char(ep.Y, sp.X)) && stepOk(g.Level.Char(sp.Y, ep.X)) } // cansee returns true if the hero can see a certain coordinate (chase.c // cansee). func (g *RogueGame) cansee(y, x int) bool { p := &g.Player if p.On(IsBlind) { return false } if distance(y, x, p.Pos.Y, p.Pos.X) < LampDist { if g.Level.FlagsAt(y, x).Has(FPass) { if y != p.Pos.Y && x != p.Pos.X && !stepOk(g.Level.Char(y, p.Pos.X)) && !stepOk(g.Level.Char(p.Pos.Y, x)) { return false } } return true } // We can only see if the hero is in the same room as the coordinate // and the room is lit, or if it is close. rer := g.roomin(Coord{X: x, Y: y}) return rer == p.Room && !rer.Flags.Has(IsDark) } // findDest finds the proper destination for the monster (chase.c // find_dest). func (g *RogueGame) findDest(tp *Monster) *Coord { prob := monsterTable[tp.Type-'A'].Carry if prob <= 0 || tp.Room == g.Player.Room || g.seeMonst(tp) { return &g.Player.Pos } for _, obj := range g.Level.Objects { if obj.Type == Scroll && obj.Which == SScare { continue } if g.roomin(obj.Pos) == tp.Room && g.rnd(100) < prob { claimed := false for _, other := range g.Level.Monsters { if other.Dest == &obj.Pos { claimed = true break } } if !claimed { return &obj.Pos } } } return &g.Player.Pos }