Scrolling Rooms

by Ron Gilbert
Feb 02, 2015

Another Thimbleweed Monday and time for a new video and some source code.

I spent last week working on getting rooms implemented and it closely follows how they were set up in SCUMM.  In SCUMM I had the luxury of writing my own compiler, so I could customize the syntax to my liking.

Since we're using Squirrel for Thimbleweed Park, I needed to figure how to do something similar, yet retain what I liked about how rooms were set up in SCUMM. I've come pretty close, and I might make some small changes to the Squirrel compiler to make the syntax a little clearer. By and large, it's working quite well.

There is probably a better way to do all this, but this is a trip down memory lane for me as well, so I wanted to have it be as close as I could to making a classic SCUMM game. I even pulled up the original Maniac Mansion SCUMM engine source code to see how it handled the camera.

Cameras are always something you spend the entire project tweaking and I spent an afternoon working on it, trying to get it to feel right. I'm not 100% sold yet. This camera drifts a little too much and could use a slight acceleration when it starts moving. The camera in the early SCUMM games are pretty crude. Everything had to scroll on 8-pixel boundaries due to the C64 character set and then later on due to the compression we used in the PC. We could have jumped through some hoops to get it working, but performance was a huge issue and we were pushing it as it was.

There is no pathfinding at the moment (it relies on the honor system) and I'm not sure how I'm going to implement it. I could do an old SCUMM style walk-box system, or maybe bitmap based with A*. I probably won't get to pathfinding for a month, it's not critical at this point.

The video is driven by the script below with minimal hand waving.

class Bank
{
    background = "Bank"  // the data file to load (Bank.json)

    // Called when the room is entered, after objects are inited.
    enter = function()
    {
    }

    // Called when exiting, before objects are destroyed and new room's enter code is run
    exit = function()
    {
    }

    objects =
    {
        bankPainting =
        {
            name = "Cheap Painting"
            lookAt = function()
            {
                currentActor.say("I'd think a bank could afford a better painting.")    // .say() not working yet
            }
        }

        bankToMainStreetDoor =
        {
            name = "Door"
            quickExit = true    // Actor exits before fully reaching the door
            walkTo = function()
            {
            }
        }
    }
}

newRoom(Bank);

detective <- Actor("DetectiveFemaleWalk")
detective.room(Bank)
detective.at(160,10)

cameraFollow(detective)

One thing you'll notice is there are raw strings in the source code and for translation reasons this is a really bad idea. I'll do a full post in a few months talking about strings, rapid development, translations and how we're going to handle it. Until then, hold your eye rolling and self-righteous distain.

In addition to the above source code, each room has an additional file that defines it's visual characteristics, including images, hotspots, and walk areas.  The files are in JSON format, which I find amusing since JSON was created by Doug Crockford, who Gary and I worked with at Lucasfilm and he produced the NES port of Maniac Mansion. In addition to JSON being a great data format, it's my little thanks and nod to Doug.

Right now I edit these files by hand, but I'll be writing a GUI editor in April that will allow the game programmers and artists to define everything visually. The editor will read and write these files as I find it very convenient to have editor data files in a format that I can bring into a text editor and made big changes or massive find/replaces. Sometimes the best tool for the job is notepad.

name: "Bank"
background: "Bank"
objects: {
    bankPainting: {
        usepos: "{83,26}"
        usedir: FACE_BACK
        hotspot: "{{40,120},{119,82}}"
    }
    bankMainStreetDoor: {
        usepos: "{25,10}"
        hotspot: "{{0,10},{20,120}}"
        cursor: CURSOR_EXIT_LEFT
    }
}
props: {
    bankRope: {
        image: "BankRope"
        pos: "{300,32}"
        zsort: 6
    }
}

Some of you may have noticed this isn't valid JSON. I wrote my own JSON parser for Scurvy Scallywags that can read compliant JSON, but also relaxes some of the rules just a little. Key's don't need to be in quotes if they are made from alpha+digits, commas are optional if they are not syntactically confusing, and the opening "{" is optional as the file is assumed to be a dictionary. Small changes, but I find them convenient.

There might not be a new video next Monday since I'm going to be gone for two days (family stuff) and then focusing on the design and getting ready for the "Big Brainstorm" with David Fox.

But the week after that, I plan on having a first pass at the UI, selecting verbs, building sentences and clicking on objects.

It really is 1987 all over again. In a whole lot of ways.

- Ron



Timmy - Feb 02, 2015 at 11:19
You sir, are the Linus Torvals of the adventure games. Amazing stuff!

Jonny - Feb 02, 2015 at 14:02
Linus is the Ron of operating systems. Or wait I don't like Linux but it doesn't matter.

mancomb seepgood - Feb 02, 2015 at 15:09
I actually think he and Gary are the Ken Thompson and Dennis Richie of point and click adventure games.

Mattias Cedervall - Feb 03, 2015 at 01:07
Linus Torvalds is actually the penguin of Madagascar. :P

Joel Dorsey - Feb 02, 2015 at 11:28
Looks fantastic so far. Can't wait to see how this game will progress.

Nuno - Feb 02, 2015 at 11:29
Awesome stuff, Ron :) Can't wait for the next blog post. Thank you for sharing all of this with us.

Mario Faross - Feb 02, 2015 at 11:29
oh yeah. that looks slick.

Jens - Feb 02, 2015 at 11:37
Isn't the old walk box system effectively a precomputed A*? Though I would probably use a triangle mesh instead of boxes nowadays.

Ron Gilbert - Feb 02, 2015 at 11:42
Basically.

If I was going to use walk-boxes today, I'd do them as full n-sided polygons, not simple rectangles like in Maniac Mansion (or trapezoids in Monkey Island).  Just painting the walk areas down is also appealing. Like everything we did back then, it was driven by how slow the processors were. That just isn't a realistic concern these days, we can do things like make our lives easier.

Miguel - Feb 02, 2015 at 12:40
One of the benefits of polygonal walk-boxes is that you can also add attributes to every box. "Z plane" comes to mind, ... since that can be helpful to determine if actors should be displayed above or behind a certain object (which in the video is already working, I guess it's based on the Y coordinate). It's also helpful to "zoom in and out" the actor.

Mattias Cedervall - Feb 03, 2015 at 01:11
Ron, have you ever considered using silly walks-boxes?

Mariano - Feb 02, 2015 at 12:11
These blog posts are amazing for aspiring game devs.

I'm wondering if you ever plan on open sourcing some of the engine. Specifically I'm thinking of the bridge between C++ and Squirrel scripting. There are so many questions online about how to implement scripting on games and your solutions seems really elegant!

Federico - Feb 02, 2015 at 12:46
Awesome. I like how simple the code looks.

Thomas Arnold - Feb 02, 2015 at 13:12
Sadly it is 1984 all over again. So do you promise that TWP will not scan our disks and phone home the findings?

Assaf - Feb 02, 2015 at 13:16
Awesome and interesting read. Thank you and good luck.

Jonny - Feb 02, 2015 at 14:08
I was just waiting for a look at the cheap painting. And I will read up on A*.

Brian S - Feb 02, 2015 at 14:13
I don't see anything in the Bank.json file which describes the hand rail, and it seems like that one needs special treatment, since are walking in front of it an behind it.  Is this handled in the Background definition, with some depth information?

Ron Gilbert - Feb 02, 2015 at 18:40
It's the prop bankRope.  It includes the rope and poles. The user never interacts with it, so it doesn't need to be tagged any finer than that. If it was something the player needed to click on, I might define the poles and the rope separately so they were't clicking on so much blank space.

Brian S - Feb 02, 2015 at 19:19
Yes, I don't know how I missed that... and there's a zsort property presumably for handling the depth and allowing the character to walk around it. Cool!

Nice stuff, Ron - very exciting to see these posts.

Mattias Cedervall - Feb 03, 2015 at 01:14
So no pole dancing? :-(

Patrick - Feb 03, 2015 at 14:45
How is the rest of the z ordering done? The bankRope has a zsort of 6 but the rest of the room has no such proerties.

Patrick - Feb 04, 2015 at 04:29
Foudn it in the other comment further down :-)

Klongeiger - Feb 02, 2015 at 14:17
I wish I wouldn't get hung up on words so much. Right now I'm just wondering what a squirrel compiler might look like.
Otherwise, great read as always. Keep up the excellent work.

Chris Subagio - Feb 02, 2015 at 14:28
I would have thought that the least friction here would be to just use a bitmap for the walkable areas. You have ample storage these days, although I'd imagine just RLE-ing each line of a 1 bit bitmap with this kind of data would be tiny anyway. Testing against the map is super cheap, and even treating each individual pixel as a nav graph node would be cheap as chips. Bonus: making arbitrarily shaped dynamic changes to the map would be really easy too, i.e. turning on or off doors and ramps and cursed giant raspberries.

Keeping the maps and the art in sync should be easier if you can get a pipeline where you do them from the same source, and it gives you a little more wiggle room to get funky with the art too, given that you'll have a 1-1 mapping between the "collision" and visuals.

You can even use more bits/more maps to cheaply flag all kinds of things, like multiple walking planes at different scales, or a the-floor-is-lava map. Because the floor is sometimes lava, except where it isn't.

LichiMan - Feb 02, 2015 at 15:00
How do you handle z sort layers? Is it all about the Y position of the character? And when there's more than one prop in the same space of the room? Do you change only the z sort of the character or do you change also the one of the all props?

Ron Gilbert - Feb 02, 2015 at 18:35
The zsort of objects that don't move never changes. The zsort of the character will often be the base of their feet, but doesn't have to be.  zsort is basiclly the y cood of the object. I call it 'z' because it's simulating depth.

LichiMan - Feb 03, 2015 at 09:59
I thought the zsort digit was something like a layer number, because the number 6 looked too low for a y-coord. But there's only 6 pixels from  the bottom of the room to the bottom of the BankRope! (zero-based) Thanks :)

Joe - Feb 02, 2015 at 16:22
Is there still a missing '}' at the end of that JSON, or is the parser also allowing that somehow too? Seems like the last '}' is either closing "props" or "bankRope", I'm not sure which.

Ron Gilbert - Feb 02, 2015 at 18:33
Yes, there was a missing } in the blog post code.  Thanks.

Stefano - Feb 02, 2015 at 16:27
Awesome post! I love how clean the Squirrel code looks like. Seeing all this built bottom up is priceless.

Christopher Griffin - Feb 02, 2015 at 17:11
I have a couple of questions:

1. I'm a programmer, and I'm simultaneously awed and inspired by the simplicity of the scripting thus far... does it ever get much more complicated, or is this really the meat of it, barring a few tricks to accommodate visuals and walk boxes?  Also, I should clarify that the tone of my question is sincere awe, not a snide "that's all there is?" remark.

2. Do you ever have to create your own placeholder art, in Gary's absence, and if so does it embarrass you to work with it?  I have that problem in my game development efforts, so I'm just hoping that I'm not alone in that regard. :D

Thanks!

Adventure Fan - Feb 02, 2015 at 18:53
From a technical point of view, I think adventure games are pretty basic and fairly non-advanced. It takes some time to set up basic features like walking, animation, inventory, item interaction, character interaction, etc. Back in the 80's, this was still difficult to do because of the technical limitations of computers in those days. But today, with modern tools, it could be done in an afternoon. But the real power of these games comes from their scripting engines. Clever use of scripting really is what drives a good adventure game, and I think the scripting part can become pretty complex with some projects. Perhaps even more complex than the hard-coded parts?

Brian S - Feb 02, 2015 at 19:26
Out of curiosity, when you are moving the character diagonally (meaning, toward the camera, and to the side at the same time), how do you decide when to switch to the front facing view vs. the side view, and will it just stick to one perspective for each target coordinate?

Peter Campbell - Feb 02, 2015 at 22:01
Regarding the graphics themselves, or to be more clear, regarding creating the graphics, how much longer would it have taken to draw and implement the painting and the clock on the wall using the tools that were available back in 1987 compared to what's available now?

Gary Winnick - Feb 03, 2015 at 13:06
Typically, the actual drawing time - when I'm pushing pixels around on the screen to create a bit mapped image is about the same as it was in the good old days (at least in my experience). The main difference on the drawing side - in regard to background image- is that in 1987 on the C64 it had to fit into a 256 tiled (character set)- as opposed to just being a straight bit map. Ron wrote a really cool program that went through and compared the characters, crunching my bit map down to the correct number by replacing similar characters with a most commonly used one. I would then go back in by hand - touch up the image - and repeat the process until we achieved the desired result. Conversely, today, I can just draw a scene and not worry about the character constraints- so that extra step has been removed.

-Gary

Peter Campbell - Feb 03, 2015 at 19:07
Mr. Winnick, first of all let me say it's really cool being able to discuss the development of Thimbleweed Park on this blog with both you and Mr. Gilbert , you guys are legends imo.  From reading what you posted, I do kinda remember Ron discussing having to deal with the C64's 256 character limit during his Maniac Mansion speech that he gave at Game Forum Germany 2011.  Having to tinker around and finagle with your art during MM's development to get everything to appear and fit properly on screen due to the C64's graphical limitations is something I imagine you don't miss one bit,, that just seems so tedious to have had to of dealt with even with the program Ron created (though I imagine it would've been 10x worse had Ron not come up with that).  Although back at LucasArts I dunno if you guys were paid by the hour so maybe it wasn't so bad lol.

Btw, regarding the "cheap painting" of the mountains, the late Bob Ross of PBS fame would approve of that!

Derrick Reisdorf - Feb 03, 2015 at 00:11
This is so groovy. I love seeing game guts.

Mattias Cedervall - Feb 03, 2015 at 00:59
Thank you for having translations in mind, Ron-san! :-) The hardest part will be to translate the graphics (posters, signs and so on) simply because the words might take up more space in Swedish for example. I actually visited a retro fair with old video games last Saturday and one guy sold posters with Maniac Mansion and Monkey Island for example. :-) I didn't buy one. Sorry.

delMar - Feb 03, 2015 at 03:07
I think there's nothing wrong with the scrolling. You may keep it, it's perfect :-)

Whenever I reached some kind of milestone (like in the video shown here, ie being able to walk an actor around in a room) in my own projects (not necessarily game programming), I feel the urgent need to click around the screen for hours and just watch it happen :-)
Along with your source-code sample, I could watch this video over and over again

Joost - Feb 03, 2015 at 03:19
I'm seeing a lot of code that may end up being repeated an awful lot of times. Have you considered subclassing items?
let's say Bank would be a subclass of MajorRoom. You'd define quickExit = true once on the MajorRoom class, and quickExit = false on MinorRoom. both of these rooms could be subclasses of Room. The Room class itself could have an array of Exits that display some default behavior, and via the infernal gift of subclassing this magically makes every MajorRoom and MinorRoom work sensibly..

The upside would be that you'd be able to focus on the specific details of a Room, and be guaranteed that the 'world' still works, unless you specifically change it for a specific room - in which case the code is there because the game needs it. Only the code that's required for the specific room would be in the specific room class file. Which is awesome, in terms of overview and separation of concern.
I find it quite annoying when game areas behave differently from eachother for no reason, except -ahem- sloppy programming. This ruins the illusion.

Ron Gilbert - Feb 03, 2015 at 12:46
Subclassing is always dangerous because the base classes can hide important functionality, especially if you're doing a lot of polymorphism.  If at some point 'quickExit' becomes the most common option, I'll flips it to have a 'slowExit'.

Everything is very early, so I'm still figuring out what's going to work best.

BTW: The 'quickExit' flag is so a character doesn't have to walk all the way to the door before leaving the room. They might walk 25% of the way, then the room transition will happen. This was a change we made in some the latter Humongous Entertainment games, and it felt really good. It eliminates a lot of walking time and since the screen is going to cut anyway, it's not jarring.

Joost - Feb 05, 2015 at 05:45
subclassing can be tricky - but I always like the consistency that you get for free with it. When you get so many subclasses that words like tree or taxonomy would apply...well... the penalties would be worse than the benefits.
On the other hand, if you don't use subclassing, you will end up fixing the same bug in 50 files and only doing it in 49 because of a phone call or a picture of a kitten on the internet...

quickExit seems like an excellent idea. This shows that you recognize the player's pain in moving back and forth a lot, and that simple pat on the virtual shoulder helps a lot to keep the game fun even though some parts can be repetetive.

GCPC - Feb 03, 2015 at 04:40
Pick up cordon barrier.
Pick up metal post.

Demetris Thoupis - Feb 03, 2015 at 06:57
Hi Ron,
Great work and love the "storyline" type of script as you mentioned many times in your blog when you/your team were creating Scumm you wanted it to be more like a movie script (not exactly in those lines but still)..
One thing only bothered me in the video and don't know if it bothered me in a good or bad way still trying to figure it out. At 00.06 seconds when you make the first stop as the camera scrolls , at the stop point it continues smoothly to scroll just a bit more and then stops. This was not achievable in Scumm I suppose but with this engine it works or doesn't work. I am confused about how I feel about it. I am not sure whether it removes the retroness of the game or not. It is such a smooth move for a game this old! :) What do you and Gary feel about it?

P.S KEEP UP THE EXCELLENT WORK!!

Regards

Demetris

Manuel Quiñones - Feb 03, 2015 at 07:37
The script has one position harcoded in pixels: detective.at(160,10).  I would expect that to be defined as a hotspot in the relaxed JSON.

Is mooving! Great!

Ron Gilbert - Feb 03, 2015 at 12:35
Yes, this is all just test code, but in the final game, "important" spots in the rooms will get edited using the GUI so they can be moved quickly. Also, 160,10 is not screen coods, they are room coords and won't change with the display or device resolution

Nick - Feb 03, 2015 at 15:47
What are the room coords exactly?

Iron Curtain - Feb 03, 2015 at 22:51
I think "Room Coords" are coördinates of the room in question and not the particular display, such as on a monitor or an Oculus-Rift-like VR device.

Tomimt - Feb 03, 2015 at 08:51
It is fasicnating to see how you're putting things together. This whole blog itself seems to be turning into a nicely grounded mini game development class.

Mafti - Feb 04, 2015 at 14:52
Kudos to getting a peek into your brains!

now for a poke
Are you planning to make the engine accessible for other developers, so they can create their own adventures?

Zak Phoenix McKracken - Feb 05, 2015 at 05:39
This kind of scrolling is nice, neat.
I prefer it in this way, even if in 1987 the scroll was 8-pixel wide, due to hardware restriction.
(Maybe you can start thinking on a special "1987" settings page, someone could prefer to view strict 1987 look and feel...)

Henk - Feb 05, 2015 at 06:02
Does this mean every character says the same sentice when looking at a painting?
I would be great if each actor could have a little variation in it or maybe (Don't remember what game did this) when you click multple times on the same object the character would get frustrated and say something different.

Ron Gilbert - Feb 05, 2015 at 10:34
No, this is just test code. Different actors will say different things. Not always, but when it matters, is funny, or part of their character.

Ciantic - Feb 05, 2015 at 06:50
I'd like to say, keep the scrolling smooth like in this video.

I didn't like the "jagged" scrolling of the 80's in Kickstarter video. I don't think the smooth scrolling takes anything away from the authenticity of the old style.

If you make it as a option, please consider setting the smooth scrolling as default. Wider audience doesn't want to see jagged scrolling as a default. Those wishing to torture themselves can set the setting to false.

LogicDeLuxe - Feb 05, 2015 at 13:08
Hi Ron, don't you still have the rights for the SCUMM engine? Why not take SCUMM V8 for this? It would safe you much of the porting effort, since ScummVM already did this to virtually any modern system.

And I wonder, why the scroll register of the C64's VIC wasn't used back then? It surely is there because of the fixed 8-width pixel chars.

Fulvio - Feb 05, 2015 at 15:15
Love the engine.
Since there is a lack of quality 2D, open-source, point and click  adventure engines in the wild, is there any chance (even remote) to see it someday on GitHub? :)
That would make me (and I guess many developers) very happy. :)

Jeff - Feb 09, 2015 at 15:36
Any chance we could interest you in open sourcing that JSON library / parser?

Julian - Jan 12, 2016 at 11:22
Squirrel? Nooo, haha. I have some experience working with it from L4D2's EMS (Extended Mutation System). My bad memories are mostly due to horrible documentation on Valve's side, though. Hopefully it's not bad as a standalone language.