Log In  

Hello everyone!

I have an idea for a roguelike dungeon game and I'm trying to flesh out ideas for map generation. Unlike a lot of roguelikes where the entire map is procedurally generated per floor, I wanted to create a set of rooms that are selected from as you move through the game. Each room would essentially be a pre-generated layout, but randomly chosen as the player passes through a new door (think tabletop games like Betrayal with room tiles.) The edge of map space would be like your deck of room tiles. As you move, you flip a new tile onto the board, building the map as you go.

My plan was to design each room at the edges of the map space, and then sort of copy and paste them into the center of the map space as needed. Something like the diagram below:

0--2--4--1--?

the player would start in the room (0) and as they moved through the door, the next room (1 through 4) would be selected from the list of rooms and added in line for the player to enter. Basically, I'm just trying to figure out the best way to store a room (maybe as an array or string) and redraw it in a different location on the map.

I should add that I'm still pretty new to PICO development, so your ideas may go over my head a bit lol. But any input would still be appreciated!

P#112935 2022-06-09 14:44

Instead of storing the map and redrawing, I'd just move where the map() draws to show the room you want.

Use the map() function in your _draw() loop and you can specify which part of the map to show. See the manual for syntax. You can show the first map at 0,0 and then when you hit the edge of the screen, change the map to show the next screen, like 16,0

So this would show the first room: map(0, 0, 0, 0, 16, 16)
This would show another: map(0, 0, 16, 0, 16, 16)

I think that would save a lot of overhead. Maps are tricky, I've struggled myself, but if I was doing what you're after, I'd randomize where the map() points. Should be easier and more efficient.

So if each room is 16x16, you reliably have 8 128x128 pixel rooms in available for your deck. But depending on your sprite needs, you could have up to 16 rooms in your deck.

P#112937 2022-06-09 16:05 ( Edited 2022-06-09 16:13)

Final Fantasy Legend 1 is a game notorious for repeating whole maps and sections of maps, @Loque. What you might do is create an array large enough to hold your map.

Then make a type of interpreter that can not only allow you to draw new map rooms but take existing map sections and rooms and copy them to new sections to expand your map and play.

Hope This Helps !

P#112939 2022-06-09 16:21

Just checked before submitting, and it seems my answer is just a more complicated version of morningtoast's. The difference is morningtoast seems be to going off your diagram, whereas my answer below is how you could mimick Betrayal.

If the emphasis is on the design of the room tiles, then I would recommend you just keep them on the map and use a table to store the arrangement. Let's say you've got a map full of 16x16 sized rooms (the size that fits on screen without doing anything fancy). You could give each room tile a set of properties like which connections they have at the start of your code, along with the data for the starting room like so:

function _init()
  room_data = {
    {"up", "down", "left", "right"},
    {"left", "right"},
    --etc.
  }
  placed_rooms = {
    {
      x=0,y=0,room=0,
      --any other data an instance of a room needs
    }
  }
  -- make a reference to the current room for quicker usage
  curr_room_tile = placed_rooms[0]
end

Then, whenever the player moves off the screen in any direction, you can search for the rooms that have the needed connections and lacks any connections that previous rooms dictate should be a wall. Once one is found, you could choose one with the rnd() function and add it to the table of rooms that have been placed into the game like so:

  add(placed_rooms, 
   {
     x=new_x, y=new_y, room=new_room,
    -- other data
  })

When you want to draw the room, the way to do so would then be to use the offsets that would be implied by the room number. Using 16x16 rooms going left-to-right then top-to-bottom (reading order) that would be this:

  -- 16x16 rooms would mean 8 per row. % is remainder division
  -- \ is integer division
function _draw()
  cls()
  map((curr_room_tile.room%8)*16, (curr_room_tile.room\8)*16,
    0, 0, 16, 16)
  -- then draw whatever objects are in the room that aren't
  -- on the base room tile
end

Now, there's 2 main downsides that I can think of to doing it this way. First is that you'd have to search through the list of placed room tiles a lot, since it'd need to be unordered. As such, checking if a spot already has a tile would meaning searching for a list that has the indicated spot listed. Second is that you'd be able to have an infinite map this way, which also means that your game could run of memory if you don't check to make sure the dungeon isn't getting too big.

P#112940 2022-06-09 16:36

@morningtoast Yeah, this had crossed my mind when I first started. At the very least, it may be a good place to start. This way I can get other gameplay aspects fleshed out a bit more and maybe just come back to it later. The player wouldn't be walking around the rooms like they would in Zelda. Ideally, all the player would do is select a cardinal direction to move. The game would then automatically animate the sprite running into that adjacent room and the player would deal with whatever event they encounter: turn-based combat, mini-game, treasure, etc.

@dw817 I think my biggest issue right now is my inexperience with the platform and lua coding in general. I've never done anything involving arrays and map tiles. Would I just be holding a sprite number and x,y coordinates in each element of the array? Or is there some way to store values related to the map itself?

P#112942 2022-06-09 16:45

@kimiyoribaka That is definitely a lot for me to process lol. But I think I understand it well enough to try it out! The upside is that the player won't be able to return to previous rooms. Looking at it now from a different perspective, I wouldn't technically even need the option to choose a direction. Since you can never go backwards, I could just make it a straight line like in my original diagram. Run into room, beat objective, collect loot, run into next room, repeat until boss. It would definitely save me a lot of frustration lol. I like the idea of directional choice, as it feels more like a realistic dungeon, but the choice seems to serve no purpose beyond that. So I may just scrap it altogether.

P#112944 2022-06-09 16:57 ( Edited 2022-06-09 17:08)

Ah heck, if the game is linear then I think moving the map camera - map() - is the lowest friction. Draw your maps for each screen as you want, then show the section of the map as you go.

Only random thing you'd need is pulling from an array of room numbers, like Kimi outlined. And passing an array to rnd() will give you back a random from that array.

all_rooms={1,2,3,4,5} --all room #s in an array
next_room=rnd(all_rooms) --gives you back a random number from the array
del(all_rooms,next_room) --removes room from the pool so you don't get same room next time
P#112953 2022-06-09 19:25

@morningtoast I think I'll keep it linear in design, for now at least. Seems like it'll just be fewer headaches all around.

Thanks for all your help everyone! Can't wait to get home from work and get started!

P#112954 2022-06-09 19:30

I made a roguelike on Pico 8 awhile ago that stores room templates as strings. Not sure it's done in the most efficient way possible, but you're welcome to steal anything from the cart that might help. https://www.lexaloffle.com/bbs/?tid=44570
I also made a separate cart that allowed me to build rooms in the map editor, and then put the room template string onto my clipboard at runtime. If you'd like, I can post that as well.

P#112955 2022-06-09 19:30

[Please log in to post a comment]

Follow Lexaloffle:          
Generated 2023-01-31 04:04:09 | 0.044s | Q:24