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
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
#3
(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
#4
Maybe the player should ... hunt ghosts ... using a weird ... proton gun?
Reply
#5
Actually there's a 3D rougelike coming to Nintendo Switch where you're an American redneck shooting monsters in Egyptian pyramids Smile
Reply
#6
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
#7
I'm terribly sorry, must have been some debug-crap that I had forgotten to remove Sad
Reply
#8
I've started building 3D stuff from the generated maps now.

There's not much diversity among the rooms yet. They're all rectangular, but some of them contains pillars and girders. This will change, trust me, and soon there'll be enemies, treasures, locked doors and stuff. 

I didn't spend much time on drawing the textures. I easily get sick of drawing - that's why I discontinue so many hobby game projects.

Zip attached Smile

[Image: spoopy_dungeons.jpg]


Attached Files
.zip   spoopy_dungeons.zip (Size: 61.5 KB / Downloads: 11)
Reply
#9
This is SO cool. You mentioned you don't like spending time on textures... May I suggest website that contain FREE textures? I usually do that sort of thing when looking for assets but resort to Gimp for the kind of stuff that "I" would need...

I am curious about performance. What would you suggest would be the minimum requirements of a PC/Laptop to run Gloom efficiently?

Great job by the way... Wink
Reply
#10
That's a problem of mine, I don't like to use the work of others, even if it's free (public domain). I just have to do it myself, it's a curse.

At the current resolution, 480p, it runs very smooth on my laptop PC. I can turn up the fps value to 120+ (SPD_HoldFrameDraw) without problems. But then it uses one (of several) 2.4GHz processor. On my 800Mhz Linux computer on the other hand, it runs at around 30fps Sad

The original GLOOM ran ok on a 30Mhz Amiga. And DOOM, also software rendering, worked well on a 50Mhz PC. But, well, naalaa is an interpreted language using 32 bit graphics (DOOM was palletized, one byte per pixel) and ... excuses, excuses, excuses Smile

Anyhow, for this game, Spoopy Dungeons, I can easily add a window resolution settings thingy, so that you can pinch it all the way down to 240p (upscaled to 480), like Wolfenstein 3D, or up to 720p.

Now I gotta make a 3D model of a key. Most enemies will look like Steve from Minecraft, because I've already made a program to render such characters in naalaa 5 Smile I use that in that other, unfinished, GLOOM game I made, Zombie Tumult.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)