Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Random dungeon generation
#1
I want to make a basic Rogue-like game using the GLOOM library. So I made an attempt to generate random room-based dungeons.

The code just messes with a discrete 2D map at the moment. It generates as many rooms as possible from a starting point. It marks all "dead ends", because if all of the dead ends contain treasures/items, the player needs (depending on the game rules) to explore every inch of a dungeon. It also marks the room that is recursively most far away from the starting point, so that this room could hold the dungeon exit.

I will also add locked areas and keys. I have to think it through though, because it should not be possible for the player to get stuck or be unable to reach any area of the map.

If you run the code, the green dot marks the starting room and the red dot the "exit". All purple dots mark rooms that are dead ends. Keep pressing space bar to generate new maps.

Even if it looks boring like this, the rooms can have much weirder shapes when I generate walls for GLOOM. The rectangles only represent the bounding boxes of every room. 

Code:
' Random dungeon generator.

set redraw off
randomize time()

do
    ' A pretty big map.
    map[64][64]

    ' AddRoom will create more rooms recursively.
    startX = sizeof(map, 0)/2
    startY = sizeof(map, 1)/2
    maxDepth = 0
    maxX = 0
    maxY = 0
    res = AddRoom(map, startX, startY, 1 + rnd(5), 1 + rnd(5), 0, maxDepth, maxX, maxY)

    ' Display map.
    set color 0, 0, 0
    cls
    set color 255, 255, 255
    for y = 0 to sizeof(map, 1) - 1
        for x = 0 to sizeof(map, 0) - 1
            if map[x][y]
                if map[x][y] = 1
                    set color 255, 255, 255
                elseif map[x][y] = 2
                    set color 255, 0, 255
                endif
                draw rect x*6, y*6, 6, 6, true
            endif
        next
    next

    set color 0, 255, 0
    draw rect startX*6, startY*6, 6, 6, true
    set color 255, 0, 0
    draw rect maxX*6, maxY*6, 6, 6, true

    redraw
    wait keydown
loop

' Return true if a room could be created. The function will try to create new
' rooms in all directions recursively.
function AddRoom(&map[][], centerX, centerY, hw, hh, recDepth, &maxDepth, &maxX, &maxY)
    xLeft = centerX - hw
    xRight = centerX + hw
    yTop = centerY - hh
    yBottom = centerY + hh

    ' Check if it's ok to put a room at the given position.
    if xLeft < 1 or xRight >= sizeof(map, 0) - 2 then return false
    if yTop < 1 or yBottom >= sizeof(map, 1) - 2 then return false
    
    for y = yTop - 1 to yBottom + 1
        for x = xLeft - 1 to xRight + 1
            if map[x][y] then return false
        next
    next
    
    ' Keep track of which room is at the largest distance from the first room.
    ' The distance is measured in number of recursive steps.
    recDepth = recDepth + 1
    if recDepth > maxDepth
        maxDepth = recDepth
        maxX = centerX
        maxY = centerY
    endif

    ' Mark room on map.
    for y = yTop to yBottom
        for x = xLeft to xRight
            map[x][y] = 1
        next
    next

    ' Keep track of the number of new rooms added from here.
    newRooms = 0

    ' Try to add a room on the left.
    w = 1 + rnd(5)
    h = 1 + rnd(5)
    if AddRoom(map, xLeft - 2 - w, centerY, w, h, recDepth, maxDepth, maxX, maxY)
        newRooms = newRooms + 1
        ' Create corridor.
        for x = centerX - 1 downto xLeft - 2 - w + 1
            map[x][centerY] = 1
        next
    endif

    ' Try to add a room on the right.
    w = 1 + rnd(5)
    h = 1 + rnd(5)
    if AddRoom(map, xRight + 2 + w, centerY, w, h, recDepth, maxDepth, maxX, maxY)
        newRooms = newRooms + 1
        ' Create corridor.
        for x = centerX + 1 to xRight + 2 + w - 1
            map[x][centerY] = 1
        next
    endif

    ' Try to add a room above.
    w = 1 + rnd(5)
    h = 1 + rnd(5)
    if AddRoom(map, centerX, yTop - 2 - h, w, h, recDepth, maxDepth, maxX, maxY)
        newRooms = newRooms + 1
        ' Create corridor.
        for y = centerY - 1 downto yTop - 2 - h + 1
            map[centerX][y] = 1
        next
    endif
    
    ' Try to add a room below.
    w = 1 + rnd(5)
    h = 1 + rnd(5)
    if AddRoom(map, centerX, yBottom + 2 + h, w, h, recDepth, maxDepth, maxX, maxY)
        newRooms = newRooms + 1
        ' Create corridor.
        for y = centerY + 1 to yBottom + 2 + h - 1
            map[centerX][y] = 1
        next
    endif

    ' If newRooms is zero, this room is a dead end. A good idea would be to place
    ' an item, a treasure or something in every dead end. This would force the
    ' player to explore every inch of the map. For now, we just mark every dead end
    ' with a different value.
    if newRooms = 0
        ' Mark room on map.
        map[centerX][centerY] = 2
    endif

    return true
endfunc
Reply
#2
Nice. Everything connects as planned.
Reply
#3
Now it generates locked areas, keys and big and small treasures. 

The drawing code may seem weird. Especially, the line drawings looks clumsy. But this is because the lines will be translated to walls in GLOOM later on.

Obviously the dungeons won't be very complex, but they'll be filled with enemies, weapon upgrades and stuff, so it might still be interesting to play. I'm not a big fan of Rogue-like games myself, do they usually contain advanced puzzles?

It'll be fun to see this in 3D.


[Image: rnd_dungeon.jpg]

Code:
' Random dungeon generator.
'
' Longa korridorer saknas, men du borde kunna gora smala rum ist.

constant:
    LEFT 0
    RIGHT 1
    UP 2
    DOWN 3

visible:
    ' Globals, may be cleaned up.
    map[64][64]
    rooms?[1000]
    roomCount

    ' Globals instead of arguments for AddRoom
    maxDepth = 0
    maxDepthRoom = -1

    requiredKeys = false
    

hidden:

set redraw off
randomize time()

do
    ' A pretty big map.
    map[64][64]
    roomCount = 0

    ' AddRoom will create more rooms recursively.
    startX = sizeof(map, 0)/2
    startY = sizeof(map, 1)/2
    maxDepth = 0
    maxDepthRoom = -1
    requiredKeys = 0
    res = AddRoom(-1, 0, startX, startY, 1 + rnd(5), 1 + rnd(5), 0, 0)
    if not AddItems() then end

    ' Display discrete map.
    set color 0, 0, 0
    cls
    for y = 0 to sizeof(map, 1) - 1
        for x = 0 to sizeof(map, 0) - 1
            if map[x][y]
                if map[x][y] = 1
                    set color 64, 64, 64
                ' key.
                elseif map[x][y] = 2
                    set color 255, 128, 0
                ' big treasure
                elseif map[x][y] = 3
                    set color 255, 255, 0
                ' small treasure
                elseif map[x][y] = 4
                    set color 0, 255, 255
                endif
                draw rect x*6, y*6, 6, 6, true
            endif
        next
    next
    
    ' Draw rooms based on definitions, all walls in clockwise order. These lines
    ' represents the walls that'll be added to gloom.
    set color 255, 255, 255
    for i = 0 to roomCount - 1
        if i = 0
            set color 0, 255, 0
        elseif i = maxDepthRoom
            set color 255, 0, 255
        elseif rooms[i].locked
            set color 255, 0, 0
        else
            set color 255, 255, 255
        endif
        if rooms[i].roomLeft >= 0
            draw line rooms[i].x1*6, rooms[i].y2*6, rooms[i].x1*6, (rooms[i].y2 - rooms[i].h/2)*6
            draw line rooms[i].x1*6, (rooms[i].y1 + rooms[i].h/2)*6, rooms[i].x1*6, rooms[i].y1*6
        else
            draw line rooms[i].x1*6, rooms[i].y2*6, rooms[i].x1*6, rooms[i].y1*6
        endif
        if rooms[i].roomRight >= 0
            draw line rooms[i].x2*6, rooms[i].y1*6, rooms[i].x2*6, (rooms[i].y1 + rooms[i].h/2)*6
            draw line rooms[i].x2*6, (rooms[i].y2 - rooms[i].h/2)*6, rooms[i].x2*6, rooms[i].y2*6
            ' Corridor.
            draw line rooms[i].x2*6, (rooms[i].y1 + rooms[i].h/2)*6, (rooms[i].x2 + 1)*6, (rooms[i].y1 + rooms[i].h/2)*6
            draw line (rooms[i].x2 + 1)*6, (rooms[i].y2 - rooms[i].h/2)*6, rooms[i].x2*6, (rooms[i].y2 - rooms[i].h/2)*6
        else
            draw line rooms[i].x2*6, rooms[i].y1*6, rooms[i].x2*6, rooms[i].y2*6
        endif
        if rooms[i].roomUp >= 0
            draw line rooms[i].x1*6, rooms[i].y1*6, (rooms[i].x1 + rooms[i].w/2)*6, rooms[i].y1*6
            draw line (rooms[i].x2 - rooms[i].w/2)*6, rooms[i].y1*6, rooms[i].x2*6, rooms[i].y1*6
        else
            draw line rooms[i].x1*6, rooms[i].y1*6, rooms[i].x2*6, rooms[i].y1*6
        endif
        if rooms[i].roomDown >= 0
            draw line rooms[i].x2*6, rooms[i].y2*6, (rooms[i].x2 - rooms[i].w/2)*6, rooms[i].y2*6
            draw line (rooms[i].x1 + rooms[i].w/2)*6, rooms[i].y2*6, rooms[i].x1*6, rooms[i].y2*6
            ' Corridor.
            draw line (rooms[i].x2 - rooms[i].w/2)*6, rooms[i].y2*6, (rooms[i].x2 - rooms[i].w/2)*6, (rooms[i].y2 + 1)*6
            draw line (rooms[i].x1 + rooms[i].w/2)*6, (rooms[i].y2 + 1)*6, (rooms[i].x1 + rooms[i].w/2)*6, rooms[i].y2*6
        else
            draw line rooms[i].x2*6, rooms[i].y2*6, rooms[i].x1*6, rooms[i].y2*6
        endif
    next

    set caret 0, 400
    set color 255, 255, 255
    wln "Room count:        ", roomCount
    wln "Locked areas/keys: ", requiredKeys

    set caret 0, 440
    set color 0, 255, 0
    write "Starting room   "
    set color 255, 0, 255
    write "Exit room   "
    set color 255, 0, 0
    write "Locked area   "
    set color 255, 128, 0
    write "Key   "
    set color 255, 255, 0
    write "Big treasure   "
    set color 0, 255, 255
    write "Treasure"
    redraw
    wait keydown
loop

' Return true if a room could be created. The function will try to create new
' rooms in all directions recursively.
function AddRoom(from, fromDir, centerX, centerY, hw, hh, recDepth, keyDepth)
    xLeft = centerX - hw
    xRight = centerX + hw
    yTop = centerY - hh
    yBottom = centerY + hh

    ' Keep track of which room is at the largest distance from the first room.
    ' The distance is measured in number of recursive steps.
    recDepth = recDepth + 1
    if recDepth > maxDepth
        maxDepth = recDepth
        maxDepthRoom = roomCount
    endif

    ' Mark room on map.
    for y = yTop to yBottom
        for x = xLeft to xRight
            map[x][y] = 1
        next
    next

    room = roomCount
    roomCount = roomCount + 1

    if keyDepth
        keyDepth = keyDepth + 1
        rooms[room].locked = true
    else
        rooms[room].locked = false
    endif

    rooms[room].roomLeft = -1
    rooms[room].roomRight = -1
    rooms[room].roomUp = -1
    rooms[room].roomDown = -1

    if from >= 0
        if fromDir = LEFT
            rooms[room].roomLeft = from
        elseif fromDir = RIGHT
            rooms[room].roomRight = from
        elseif fromDir = UP
            rooms[room].roomUp = from
        elseif fromDir = DOWN
            rooms[room].roomDown = from
        endif
    endif

    rooms[room].x1 = xLeft
    rooms[room].y1 = yTop
    rooms[room].x2 = xRight + 1
    rooms[room].y2 = yBottom + 1
    rooms[room].w = xRight - xLeft + 1
    rooms[room].h = yBottom - yTop + 1
    
    ' Keep track of the number of new rooms added from here.
    newRooms = 0

    ' Try to add a room on the left.
    w = 1 + rnd(5)
    h = 1 + rnd(5)

    if CanAddRoom(xLeft - 2 - w, centerY, w, h, keyDepth)
        if keyDepth = 0 and rnd(8) = 0
            requiredKeys = requiredKeys + 1
            res = AddRoom(room, RIGHT, xLeft - 2 - w, centerY, w, h, recDepth, 1)
        else
            res = AddRoom(room, RIGHT, xLeft - 2 - w, centerY, w, h, recDepth, keyDepth)
        endif
        rooms[room].roomLeft = res
        newRooms = newRooms + 1
        ' Create corridor.
        for x = centerX - 1 downto xLeft - 2 - w + 1
            map[x][centerY] = 1
        next
    endif

    ' Try to add a room on the right.
    w = 1 + rnd(5)
    h = 1 + rnd(5)
    if CanAddRoom(xRight + 2 + w, centerY, w, h, keyDepth)
        if keyDepth = 0 and rnd(8) = 0
            res = AddRoom(room, LEFT, xRight + 2 + w, centerY, w, h, recDepth, 1)
            requiredKeys = requiredKeys + 1
        else
            res = AddRoom(room, LEFT, xRight + 2 + w, centerY, w, h, recDepth, keyDepth)
        endif
        rooms[room].roomRight = res
        newRooms = newRooms + 1
        ' Create corridor.
        for x = centerX + 1 to xRight + 2 + w - 1
            map[x][centerY] = 1
        next
    endif

    ' Try to add a room above.
    w = 1 + rnd(5)
    h = 1 + rnd(5)
    if CanAddRoom(centerX, yTop - 2 - h, w, h, keyDepth)
        if keyDepth = 0 and rnd(8) = 0
            res = AddRoom(room, DOWN, centerX, yTop - 2 - h, w, h, recDepth, 1)
            requiredKeys = requiredKeys + 1
        else
            res = AddRoom(room, DOWN, centerX, yTop - 2 - h, w, h, recDepth, keyDepth)
        endif
        rooms[room].roomUp = res
        newRooms = newRooms + 1
        ' Create corridor.
        for y = centerY - 1 downto yTop - 2 - h + 1
            map[centerX][y] = 1
        next
    endif
    
    ' Try to add a room below.
    w = 1 + rnd(5)
    h = 1 + rnd(5)
    if CanAddRoom(centerX, yBottom + 2 + h, w, h, keyDepth)
        if keyDepth = 0 and rnd(8) = 0
            res = AddRoom(room, UP, centerX, yBottom + 2 + h, w, h, recDepth, 1)
            requiredKeys = requiredKeys + 1
        else
            res = AddRoom(room, UP, centerX, yBottom + 2 + h, w, h, recDepth, keyDepth)
        endif
        rooms[room].roomDown = res
        newRooms = newRooms + 1
        ' Create corridor.
        for y = centerY + 1 to yBottom + 2 + h - 1
            map[centerX][y] = 1
        next
    endif

    ' If newRooms is zero, this room is a dead end. A good idea would be to place
    ' an item, a treasure or something in every dead end. This would force the
    ' player to explore every inch of the map. For now, we just mark every dead end
    ' with a different value.
    if newRooms = 0
        rooms[room].deadEnd = true
    else
        rooms[room].deadEnd = false
    endif

    return room
endfunc

function CanAddRoom(centerX, centerY, hw, hh, keyDepth)
    xLeft = centerX - hw
    xRight = centerX + hw
    yTop = centerY - hh
    yBottom = centerY + hh

    ' Limit size of locked areas.
    if keyDepth
        keyDepth = keyDepth + 1
        if keyDepth > 4
            return 0
        endif
    endif

    ' Check if it's ok to put a room at the given position.
    if xLeft < 1 or xRight >= sizeof(map, 0) - 2 then return false
    if yTop < 1 or yBottom >= sizeof(map, 1) - 2 then return false
    
    for y = yTop - 1 to yBottom + 1
        for x = xLeft - 1 to xRight + 1
            if map[x][y] then return false
        next
    next
    
    return true
endfunc

function AddItems()
    ' Set item for every room to 0.
    for i = 0 to roomCount - 1
        rooms[i].item = 0
    next

    ' Add keys.
    keysLeft = requiredKeys
    while keysLeft > 0
        ' Check unlocked dead ends first
        index = -1
        res[] = SpecialRoomsWithoutItems(false, true)
        if sizeof(res)
            index = res[rnd(sizeof(res))]
        else
            ' None-dead ends.
            res[] = SpecialRoomsWithoutItems(false, false)
            if sizeof(res)
                index = res[rnd(sizeof(res))]
            endif
        endif
        if index < 0
            ' This just shouldn't be possible ...
            return false
        else
            rooms[index].item = 1
        endif
        keysLeft = keysLeft - 1
    wend   

    ' Add big treasures/arbitrary important items to all locked dead ends.
    res[] = SpecialRoomsWithoutItems(true, true)
    for i = 0 to sizeof(res) - 1
        rooms[res[i]].item = 2
    next

    ' Also add big treasures in non-locked dead ends
    res[] = SpecialRoomsWithoutItems(false, true)
    for i = 0 to sizeof(res) - 1
        rooms[res[i]].item = 2
    next

    ' Add small treasures in SOME non-special rooms
    res[] = RoomsWithoutItems()
    count = sizeof(res)/4
    if count
        for i = 0 to count - 1
            res[] = RoomsWithoutItems()
            index = res[rnd(sizeof(res))]
            rooms[index].item = 3
        next
    endif

    ' Mark on discrete map too.
    for i = 0 to roomCount - 1
        item = rooms[i].item
        if item
            x = (rooms[i].x1 + rooms[i].x2)/2
            y = (rooms[i].y1 + rooms[i].y2)/2
            ' key
            if item = 1
                map[x][y] = 2
            ' big
            elseif item = 2
                map[x][y] = 3
            ' small
            elseif item = 3
                map[x][y] = 4
            endif
        endif
    next

    return true
endfunc

function SpecialRoomsWithoutItems[](locked, deadEnd)
    count = 0
    for i = 0 to roomCount - 1
        if rooms[i].locked = locked and rooms[i].deadEnd = deadEnd and rooms[i].item = 0
            count = count + 1
        endif
    next
    result[]
    if count > 0
        result[count]
        count = 0
        for i = 0 to roomCount - 1
            if rooms[i].locked = locked and rooms[i].deadEnd = deadEnd and rooms[i].item = 0
                result[count] = i
                count = count + 1
            endif
        next
    endif
    return result
endfunc

function RoomsWithoutItems[]()
    count = 0
    for i = 0 to roomCount - 1
        if rooms[i].item = 0
            count = count + 1
        endif
    next
    result[]
    if count > 0
        result[count]
        count = 0
        for i = 0 to roomCount - 1
            if rooms[i].item = 0
                result[count] = i
                count = count + 1
            endif
        next
    endif
    return result
endfunc
Reply
#4
Awesome!!!!

This game will be playable for years.
Reply
#5
(04-28-2018, 01:56 PM)Rick3137 Wrote: Awesome!!!!

This game will be playable for years.

Should it be fantasy or scifi themed? Fantasy is probably goodlier for a roguelike
Reply
#6
(04-28-2018, 05:28 PM)Marcus Wrote:
(04-28-2018, 01:56 PM)Rick3137 Wrote: Awesome!!!!

This game will be playable for years.

Should it be fantasy or scifi themed? Fantasy is probably goodlier for a roguelike

 I would not choose between these. They are already linked together in most book sores.
We may even think up some new ones.
Reply
#7
Maybe the player should ... hunt ghosts ... using a weird ... proton gun?
Reply
#8
Actually there's a 3D rougelike coming to Nintendo Switch where you're an American redneck shooting monsters in Egyptian pyramids Smile
Reply
#9
(04-28-2018, 08:20 PM)Marcus Wrote: Actually there's a 3D rougelike coming to Nintendo Switch where you're an American redneck shooting monsters in Egyptian pyramids Smile

 I've read whole novels, about tomb raiders. Good selection.
Reply
#10
Oh dear. All I get is a black screen... I changed nothing... Copy and past. Save file and run. I looked to see if perhaps a 'font' issue... (I don't have all of Windows' fonts installed on my Linux machine) But it seems that fonts aren't used... Any suggestions?

J

Never mind... I thought maybe I was impatient.. I waited a little longer... Nothing... It wouldn't be as simple as hitting the space bar, would it? Nah.. So I did... Up comes the map... I won't say anything if you don't... lol

By the way. Brilliant idea. Well done!
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)