Decompose rooms.go (refactor step 7)

digRooms splits into digRoom/placeGoneRoom/placeMazeRoom/
placeNormalRoom/roomGold/roomMonster; dig gains digPick/digWallGap;
findFloorImpl gains floorChar; enterRoom and leaveRoom gain per-cell
helpers. rooms.go is complexity-clean. Behavior and RNG call order
unchanged.
This commit is contained in:
2026-07-22 21:59:24 +07:00
parent bac9e361bc
commit 432ea4f019

View File

@@ -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)
}
}
}