Keywords: demo, design
(Download the complete game with source code at gughunter.2007%2D1214/

Long ago, I wrote a little game called PantsQuest. It wasn't a very good game, but it was a pretty decent demo of how you could overlay graphics on objects using BYOND's image() function. Then Dantom added overlays to BYOND, and made my demo obsolete -- not just obsolete, but broken, because "overlays" became a reserved word! So, whenever you use overlays, take a moment to think of that poor little man running around without any pants. On second thought, no, I'm not recommending that.

Today I introduce you to PantsQuest II. Like its predecessor, it isn't a very good game, but you may find it a useful demonstration of a clever technique. I wrote PantsQuest II as a test to see if it was possible to handle "cut scenes" easily in BYOND. You may or may not know the term, but you know what cut scenes are: when you're playing a game and you suddenly lose all ability to control your character, and a little bit of dialogue and/or action plays out while you watch helplessly, that's a cut scene. Some people dislike cut scenes, and skip them whenever possible, but not me (unless it's the third, or maybe tenth, time I've seen the cut scene in the last half-hour).

Cut scenes are potentially a tricky proposition in BYOND games, even when there's only a single player, as in this demo. (Cut scenes in multiplayer games would be a whole 'nother can of worms -- you don't want to freeze everyone in the world for one guy's cut scene!) As it turned out, in creating a framework to support cut scenes, I pretty much had to support missions and AI as part of the package. (AI is "Artificial Intelligence" -- the code that governs computer-controlled creatures.) Let me take you on a brief tour of the thinking that led to PantsQuest II as we know it today.

I started with a basic goal:

* Write code to support cut scenes in a BYOND game.

This entailed several other goals:

1) Ignore player input during cut scenes (but allow ENTER to advance the dialogue).
2) Disable mob AI during cut scenes.
3) Make it easy to script cut scenes.
4) Come up with a script for a simple game that demonstrates the technique.

My implementation began with Mr. Clock, which is an actual object in the PQII code. You probably know that BYOND has its own internal timer, and it counts 10 "ticks" to the second. This means you won't be writing a 60FPSFPS (60 Frames-Per-Second First-Person Shooter) in BYOND, but it also means that you have a good amount of control over when things happen. For this project, BYOND's tick-based timer made it easy to seize control of everything in the game.

"Mr. Clock" has a main process that runs once per tick. Each tick it looks through its list of "brains" and tells each one to do its thing. Each brain's Think() proc is designed to run in that context: what should it do during this single tick of the clock?

Note that Mr. Clock is the sole initiator of each brain's activity, and a brain won't think during a given tick unless Mr. Clock tells it to. That makes it pretty easy to disable AI during cut scenes; Mr. Clock ignores a brain and it shuts up. There's an ActionAllowed() proc that we can insert wherever player input needs to be regulated -- in this case, we use it in client.Move(), Click(), and KeypadEnter(). (That last one is called by a macro assigned to the Enter key, as you can see by opening the .dmf file; this method of macro definition is one of the handy new features in BYOND 4.)

That takes care of the first two items on the list. For the third item, "Make it easy to script cut scenes," we need to provide functions that can handle the most common cut scene actions with a minimum of typing. I won't go into detail about the functions, like CSWalk and CSState, here because you can see how they're used in the program's script, but I should explain the underlying principles of a cut scene. Each cut scene is really just a specialized type of brain. When one is created, it sets a flag that tells Mr. Clock to execute the cut scene and ignore all the other brains. While a cut scene is running, we actually have three levels of timers running in BYOND:

1) BYOND's standard tick-based timer.

2) Mr. Clock's game timer, which usually increases in sync with BYOND's timer, but stops incrementing while a cut scene is running. Mr. Clock is still checking his list every tick -- he has to, because the cut scene is itself a brain on his list.

3) The cut scene's internal timer, which starts at tick 0 and increases until all the scripted events have played out. (Starting at tick 0, instead of whatever number is currently in Mr. Clock's game timer, makes it easy to plan out all events at precise times relative to the start of the scene.)

As for Goal 4, "Come up with a script for a simple game that demonstrates the technique" -- well, you can see for yourself how much work and thought went into the game's actual plot and dialogue. The less said about that, the better.

Please feel free to fold, spindle, or mutilate this project in any way you like. Add or remove missions and cut scenes. Take the icons and code to use in your own games. (If you look at, you'll notice that it contains nothing that's specific to PantsQuest II, so you should be able to drop it into a completely different game without too much sweat.) I may be revisiting Mr. Clock down the road for future projects, so if you spot any bugs, I'd be interested to hear about them.
Very cool! This will be very useful for missions, and rpg type games.

Going through the quest really reminds me of those old gba games ^^.

Thanks for this demo alot man!
I actually put this into a small rpg game I am making, but there is one problem,

whatever moves doesnt change directions, or activate enter() command, or such and can go through walls. So im think the Plop proc is just like using the locate() proc. Your icons on pantsquest dont get affected by it though, becasue they dont have directions xD
whatever moves doesnt change directions, or activate enter() command, or such and can go through walls.

That's a good point. The movement is handled by cutevent/csstep/Execute(), which just sets the mob's loc, avoiding all the direction changes and Enter() checks performed by mob.Move(). If you change that line to use mob.Move() instead, you would get all that -- though personally, in my own cut scenes, I'd rather allow as few unknown factors as possible! Alternately, you could create a CSDir() scripting command to change direction as needed.