If you're wondering how the levels seem to be longer than would fit in the map, that's because levels are made of "screens", which can be specified in a list in any order, and repeated. A screen is an 8x8 tiled area of the map; 8x8 tiles is what fits on the screen in 64x64 mode.
So I make the level one "screen" at a time, and think about how they can be mixed and matched to make a longer level. By mixing up the order, you can get a convincing amount of variety with the different ways they match up, with only a little bit of noticeable repetition to the player. The parts within each screen may be recognised, but the parts where they join all look different as different screens are mixed and matched together.
I thought this was acceptable because looking at the original levels, (below) there is a fair bit of repetition, not sure if due to being clever and re-using level data (the way I am) or being lazy and copy pasting sections of the level and making a few alterations!
Indexing the map screens
So, a more technical explanation of how the screens are put together to make a level.
There is a function called LOAD_LEVEL, which, depending on the parameter, will set a bunch of variables depending on what level is being loaded, including setting the SCREENS array, which is a 1 dimensional array of integers, each number referring to a screen in the map.
Basically I split the Pico8 map up into “screens” that are 8x8 tiles big, and are indexed as if they are a linear array (you know, the thing where you can use a single number to reference a grid, if you know the width of the grid, and you divide by the number of rows to get the Y, and get the remainder to get the X). The table below shows how the map is split into screens and how they are indexed.
Shuffling the screens
We're going to now talk in terms of these "screen index positions" up there. Mainly 0, and 1. Remember each of these represent an area of 8x8 tiles in Pico-8's map data. The player always starts in screen 0. In rightwards scrolling levels, they move rightwards into screen 1, the camera following them, and when they go beyond screen 1, a "shuffle" happens, where the map data in screen 1 position is "copied and pasted" to screen 0 position, losing screen 0 in the process, and a new screen is "pasted" into screen 1 position. That's what the FUNCTION SHUFFLE_SCREEN does in the code. Also, when this happens, all the objects, and the camera, are moved back one screen's worth - simply subtract 64 pixels from their X axis. Whenever this "shuffle" happens, it happens in the blink of an eye and you don't notice it, giving the illusion of smooth scrolling on a very long level that could not possibly fit in the width of the map, let alone all of them fit in the space provided in Pico-8's map data.
Better to show it in action. Below is a recording of the game window zoomed out to 128x128 which fits the 2 screens (index 0 and 1) on the screen at once, at the top. The lower half of the screen is just whatever map data happens to be there. In fact if you look at the lower half of the screen, you can see it doesn't change, demonstrating that the top 2 screens are being changed.
SHUFFLE_SCREEN makes use of a function called LOAD_SCREEN which takes a screen index (of the map above), and copies that 8x8 area of those tiles to a destination screen index.
The castle levels do not scroll, and allow you to enter and exit each screen from any of the 4 sides, allowing you to explore the castle at your leisure. This is much simpler; the LOAD_SCREEN is used to just copy the map data from the relevant screen index, into screen index position 0, and the player never leaves this screen - when they exit from one of the sides, the new screen data is pasted in, and the player's position is inverted to be coming in from the opposite side of the screen.
The game knows which scrolling method to use by checking CUR_SCROLL_DIR. In rightwards levels, it's set to DIR.RIGHT, in the non-scrolling "4 way" castle levels, it's set to DIR.S4WAY, and in the first downwards scrolling level, surprise surprise, it's set to DIR.DOWN. These are the only scrolling directions supported by the game, to cut down on token usage. There is only one level that scrolled left in the original game, which is a helicopter level near the end (the really hard one with the stone and swamp tiles and red balls), and there wasn't a need for it to scroll that way, if I wanted to save tokens that is.
The only other remaining mystery to unveil is - if I'm constantly pasting new screens to screen index 0 and 1, what about the screens that were already there? If you open it up in Pico-8's map editor, you can see they contain the first screen of level 1, and the first screen of one of the water levels. Well... I make a backup copy of all the tiles in that corner of the map into into a simple 2D array of integers, where it sits during the whole game (using up RAM, yes), and is re-loaded back in whenever you go to a new level :)
This "shuffling" process is actually used in old 2D games, but at a finer level - single columns of tiles are scrolled onto the screen at a time, rather than a whole screens worth of tiles. This is because there wasn't a lot of video memory to work with, it keeps the cost of the hardware down. Even modern 3D games do this to some extent - yes. Only the open world games though. The thing is, in an open world game, when you move too far away from the world position of 0,0,0, you get "floating point errors" were the vertex positions of the meshes are now too big, and floating point accuracy is lost, and things start wobbling around and cracks form between meshes that should be perfectly lined up. So every so often, the whole world, all the static terrain, all the moving objects, are re-centered around 0,0,0 every so often. There is no sudden jolt when this happens, it's perfectly seamless. Moving meshes is very easy for the computer to do. You can see some screenshots of the problem in my other blog post here,