diff --git a/game/rooms.go b/game/rooms.go index 7dc9e36..6dcc78a 100644 --- a/game/rooms.go +++ b/game/rooms.go @@ -38,87 +38,119 @@ func (g *RogueGame) digRooms() { } // dig and populate all the rooms on the level for i := range g.Level.Rooms { - rp := &g.Level.Rooms[i] - // Find upper left corner of box that this room goes in - top := Coord{X: (i%3)*bsze.X + 1, Y: (i / 3) * bsze.Y} + g.digRoom(i, bsze) + } +} - if rp.Flags.Has(Gone) { - // Place a gone room. Make certain that there is a blank line - // for passage drawing. - for { - rp.Pos.X = top.X + g.rnd(bsze.X-2) + 1 - rp.Pos.Y = top.Y + g.rnd(bsze.Y-2) + 1 - rp.Max.X = -NumCols +// digRoom digs and populates one room (the loop body of rooms.c +// do_rooms). +func (g *RogueGame) digRoom(i int, bsze Coord) { + rp := &g.Level.Rooms[i] + // Find upper left corner of box that this room goes in + top := Coord{X: (i%3)*bsze.X + 1, Y: (i / 3) * bsze.Y} - rp.Max.Y = -NumLines - if rp.Pos.Y > 0 && rp.Pos.Y < NumLines-1 { - break - } - } + if rp.Flags.Has(Gone) { + g.placeGoneRoom(rp, top, bsze) - continue + return + } + // set room type + if g.rnd(10) < g.Depth-1 { + rp.Flags.Set(Dark) // dark room + + if g.rnd(15) == 0 { + rp.Flags = Maze // maze room } - // set room type - if g.rnd(10) < g.Depth-1 { - rp.Flags.Set(Dark) // dark room + } + // Find a place and size for a random room + if rp.Flags.Has(Maze) { + placeMazeRoom(rp, top, bsze) + } else { + g.placeNormalRoom(rp, top, bsze) + } - if g.rnd(15) == 0 { - rp.Flags = Maze // maze room - } + g.drawRoom(rp) + g.roomGold(rp) + g.roomMonster(rp) +} + +// placeGoneRoom places a gone room, making certain that there is a +// blank line for passage drawing (rooms.c do_rooms). +func (g *RogueGame) placeGoneRoom(rp *Room, top, bsze Coord) { + for { + rp.Pos.X = top.X + g.rnd(bsze.X-2) + 1 + rp.Pos.Y = top.Y + g.rnd(bsze.Y-2) + 1 + rp.Max.X = -NumCols + + rp.Max.Y = -NumLines + if rp.Pos.Y > 0 && rp.Pos.Y < NumLines-1 { + return } - // Find a place and size for a random room - if rp.Flags.Has(Maze) { - rp.Max.X = bsze.X - 1 + } +} - rp.Max.Y = bsze.Y - 1 - if rp.Pos.X = top.X; rp.Pos.X == 1 { - rp.Pos.X = 0 - } +// placeMazeRoom sizes a maze room to fill its box (rooms.c do_rooms). +func placeMazeRoom(rp *Room, top, bsze Coord) { + rp.Max.X = bsze.X - 1 - if rp.Pos.Y = top.Y; rp.Pos.Y == 0 { - rp.Pos.Y++ - rp.Max.Y-- - } - } else { - for { - rp.Max.X = g.rnd(bsze.X-4) + 4 - rp.Max.Y = g.rnd(bsze.Y-4) + 4 - rp.Pos.X = top.X + g.rnd(bsze.X-rp.Max.X) + rp.Max.Y = bsze.Y - 1 + if rp.Pos.X = top.X; rp.Pos.X == 1 { + rp.Pos.X = 0 + } - rp.Pos.Y = top.Y + g.rnd(bsze.Y-rp.Max.Y) - if rp.Pos.Y != 0 { - break - } - } + if rp.Pos.Y = top.Y; rp.Pos.Y == 0 { + rp.Pos.Y++ + rp.Max.Y-- + } +} + +// placeNormalRoom rolls a place and size for an ordinary room (rooms.c +// do_rooms). +func (g *RogueGame) placeNormalRoom(rp *Room, top, bsze Coord) { + for { + rp.Max.X = g.rnd(bsze.X-4) + 4 + rp.Max.Y = g.rnd(bsze.Y-4) + 4 + rp.Pos.X = top.X + g.rnd(bsze.X-rp.Max.X) + + rp.Pos.Y = top.Y + g.rnd(bsze.Y-rp.Max.Y) + if rp.Pos.Y != 0 { + return } + } +} - g.drawRoom(rp) - // Put the gold in - if g.rnd(2) == 0 && (!g.HasAmulet || g.Depth >= g.MaxDepth) { - gold := newObject() - rp.GoldVal = g.goldCalc() - gold.GoldValue = rp.GoldVal - rp.Gold, _ = g.findFloorIn(rp, 0, false) - gold.Pos = rp.Gold - g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold) +// roomGold maybe puts a gold pile in the room (rooms.c do_rooms). +func (g *RogueGame) roomGold(rp *Room) { + if g.rnd(2) != 0 || (g.HasAmulet && g.Depth < g.MaxDepth) { + return + } - gold.Flags = Stackable - gold.Group = goldGrp - gold.Kind = KindGold - g.Level.AddObject(gold) - } - // Put the monster in - prob := 25 - if rp.GoldVal > 0 { - prob = 80 - } + gold := newObject() + rp.GoldVal = g.goldCalc() + gold.GoldValue = rp.GoldVal + rp.Gold, _ = g.findFloorIn(rp, 0, false) + gold.Pos = rp.Gold + g.Level.SetChar(rp.Gold.Y, rp.Gold.X, Gold) - if g.rnd(100) < prob { - tp := &Monster{} - mp, _ := g.findFloorIn(rp, 0, true) - g.newMonster(tp, g.randMonster(false), mp) - g.givePack(tp) - } + gold.Flags = Stackable + gold.Group = goldGrp + gold.Kind = KindGold + g.Level.AddObject(gold) +} + +// roomMonster maybe puts a monster in the room; gold attracts them +// (rooms.c do_rooms). +func (g *RogueGame) roomMonster(rp *Room) { + prob := 25 + if rp.GoldVal > 0 { + prob = 80 + } + + if g.rnd(100) < prob { + tp := &Monster{} + mp, _ := g.findFloorIn(rp, 0, true) + g.newMonster(tp, g.randMonster(false), mp) + g.givePack(tp) } } @@ -182,63 +214,76 @@ func (g *RogueGame) digMaze(rp *Room) { // dig digs out from around where we are now, if possible (rooms.c dig). func (g *RogueGame) dig(y, x int) { m := &g.maze - del := [4]Coord{{X: 2, Y: 0}, {X: -2, Y: 0}, {X: 0, Y: 2}, {X: 0, Y: -2}} for { - cnt := 0 - - var nexty, nextx int - - for _, cp := range del { - newy := y + cp.Y - - newx := x + cp.X - if newy < 0 || newy > m.maxy || newx < 0 || newx > m.maxx { - continue - } - - if g.Level.FlagsAt(newy+m.starty, newx+m.startx).Has(FPassage) { - continue - } - - if cnt++; g.rnd(cnt) == 0 { - nexty = newy - nextx = newx - } - } - - if cnt == 0 { + nexty, nextx, ok := g.digPick(y, x) + if !ok { return } g.accountMaze(y, x, nexty, nextx) g.accountMaze(nexty, nextx, y, x) - var pos Coord - if nexty == y { - pos.Y = y + m.starty - if nextx-x < 0 { - pos.X = nextx + m.startx + 1 - } else { - pos.X = nextx + m.startx - 1 - } - } else { - pos.X = x + m.startx - if nexty-y < 0 { - pos.Y = nexty + m.starty + 1 - } else { - pos.Y = nexty + m.starty - 1 - } - } - - g.putPassage(pos) - pos.Y = nexty + m.starty - pos.X = nextx + m.startx - g.putPassage(pos) + g.putPassage(digWallGap(m, y, x, nexty, nextx)) + g.putPassage(Coord{Y: nexty + m.starty, X: nextx + m.startx}) g.dig(nexty, nextx) } } +// digPick reservoir-picks the next unvisited maze cell; ok is false +// when the digger is boxed in (the candidate scan of rooms.c dig). +func (g *RogueGame) digPick(y, x int) (int, int, bool) { + m := &g.maze + del := [4]Coord{{X: 2, Y: 0}, {X: -2, Y: 0}, {X: 0, Y: 2}, {X: 0, Y: -2}} + + cnt := 0 + + var nexty, nextx int + + for _, cp := range del { + newy := y + cp.Y + + newx := x + cp.X + if newy < 0 || newy > m.maxy || newx < 0 || newx > m.maxx { + continue + } + + if g.Level.FlagsAt(newy+m.starty, newx+m.startx).Has(FPassage) { + continue + } + + if cnt++; g.rnd(cnt) == 0 { + nexty = newy + nextx = newx + } + } + + return nexty, nextx, cnt != 0 +} + +// digWallGap picks the wall square to knock out between two maze cells +// (rooms.c dig). +func digWallGap(m *mazeState, y, x, nexty, nextx int) Coord { + var pos Coord + if nexty == y { + pos.Y = y + m.starty + if nextx-x < 0 { + pos.X = nextx + m.startx + 1 + } else { + pos.X = nextx + m.startx - 1 + } + } else { + pos.X = x + m.startx + if nexty-y < 0 { + pos.Y = nexty + m.starty + 1 + } else { + pos.Y = nexty + m.starty - 1 + } + } + + return pos +} + // accountMaze accounts for maze exits (rooms.c accnt_maze). func (g *RogueGame) accountMaze(y, x, ny, nx int) { sp := &g.maze.maze[y][x] @@ -278,13 +323,20 @@ func (g *RogueGame) findFloorIn(rp *Room, limit int, monst bool) (Coord, bool) { return g.findFloorImpl(rp, limit, monst, false) } +// floorChar is what an empty spot looks like in a room: passage in a +// maze, floor otherwise (rooms.c find_floor). +func floorChar(rp *Room) byte { + if rp.Flags.Has(Maze) { + return Passage + } + + return Floor +} + func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Coord, bool) { var compchar byte if !pickroom { - compchar = Floor - if rp.Flags.Has(Maze) { - compchar = Passage - } + compchar = floorChar(rp) } cnt := limit @@ -297,11 +349,7 @@ func (g *RogueGame) findFloorImpl(rp *Room, limit int, monst, pickroom bool) (Co if pickroom { rp = &g.Level.Rooms[g.randomRoom()] - - compchar = Floor - if rp.Flags.Has(Maze) { - compchar = Passage - } + compchar = floorChar(rp) } cp := g.randomPos(rp) @@ -330,34 +378,44 @@ func (g *RogueGame) enterRoom(cp Coord) { g.move(y, rp.Pos.X) for x := rp.Pos.X; x < rp.Max.X+rp.Pos.X; x++ { - tp := g.Level.MonsterAt(y, x) - - ch := g.Level.Char(y, x) - if tp == nil { - if g.inch() != ch { - g.addch(ch) - } else { - g.move(y, x+1) - } - } else { - tp.OldCh = ch - if !g.seeMonst(tp) { - if p.On(SenseMonsters) { - g.standout() - g.addch(tp.Disguise) - g.standend() - } else { - g.addch(ch) - } - } else { - g.addch(tp.Disguise) - } - } + g.enterRoomCell(y, x) } } } } +// enterRoomCell draws one square of a room being lit on entry (the loop +// body of rooms.c enter_room). +func (g *RogueGame) enterRoomCell(y, x int) { + tp := g.Level.MonsterAt(y, x) + + ch := g.Level.Char(y, x) + if tp == nil { + if g.inch() != ch { + g.addch(ch) + } else { + g.move(y, x+1) + } + + return + } + + tp.OldCh = ch + if g.seeMonst(tp) { + g.addch(tp.Disguise) + + return + } + + if g.Player.On(SenseMonsters) { + g.standout() + g.addch(tp.Disguise) + g.standend() + } else { + g.addch(ch) + } +} + // leaveRoom is the code for when we exit a room (rooms.c leave_room). func (g *RogueGame) leaveRoom(cp Coord) { p := &g.Player @@ -381,35 +439,43 @@ func (g *RogueGame) leaveRoom(cp Coord) { p.Room = &g.Level.Passages[*g.Level.FlagsAt(cp.Y, cp.X)&FPassNum] for y := rp.Pos.Y; y < rp.Max.Y+rp.Pos.Y; y++ { for x := rp.Pos.X; x < rp.Max.X+rp.Pos.X; x++ { - g.move(y, x) - - switch ch := g.inch(); ch { - case Floor: - if floor == ' ' { - g.addch(' ') - } - default: - // to check for monster, we have to strip out the standout - // bit (our Window returns the bare character already) - if isUpper(ch) { - if p.On(SenseMonsters) { - g.standout() - g.addch(ch) - g.standend() - - break - } - - pp := g.Level.At(y, x) - if pp.Ch == Door { - g.addch(Door) - } else { - g.addch(floor) - } - } - } + g.leaveRoomCell(floor, y, x) } } g.doorOpen(rp) } + +// leaveRoomCell hides one square of a room being left (the loop body of +// rooms.c leave_room). +func (g *RogueGame) leaveRoomCell(floor byte, y, x int) { + g.move(y, x) + + switch ch := g.inch(); ch { + case Floor: + if floor == ' ' { + g.addch(' ') + } + default: + // to check for monster, we have to strip out the standout + // bit (our Window returns the bare character already) + if !isUpper(ch) { + return + } + + if g.Player.On(SenseMonsters) { + g.standout() + g.addch(ch) + g.standend() + + return + } + + pp := g.Level.At(y, x) + if pp.Ch == Door { + g.addch(Door) + } else { + g.addch(floor) + } + } +}