Back to Top

Learn escape games programming

Locks and keys

locks and keys

I've seen quite a few real-life escape rooms, and all of them included one or more locks that needed to be unlocked by finding, and then using a key. Therefore, this article will show you some basic code that can be used for your keys and locks.

var got_key = 0;

SOUND* gotkey_wav = "gotkey.wav";

SOUND* chestopens_wav = "chestopens.wav";

We begin by defining a var named got_key. As you can see, its initial value is zero, but that will change to 1 when the player manages to find the key, and then picks it up by running into it.

Then, we define two sound effects; the first one will be played when the player picks up the key, and then second one when the chest opens.

The action below must be attached to a single key in your level. If you need to use several keys, you can quickly duplicate the code by using variables named "got_key1", "got_key2", etc. and corresponding actions named "action Key1", "action Key2", and so on.

action Key()

{

The line of code below makes the key passable; this is always the best solution for any type of key code. Why? Because if you keep the keys impassable, some other entities' movements (not necessarily the player) may be blocked by the keys' weirdly shaped collision boxes.

set (my, PASSABLE);

The next line of code stops the code until the player model is loaded, and the "player" pointer is assigned to it. Sometimes we may get away without waiting for the player to load, but this time it is mandatory to include that line of code, because the player pointer is used at the beginning of the function.

while (!player) {wait (1);}

Now that both the key and the player models are loaded, it's time to wait until the player comes closer than 50 units to the key.

while (vec_dist (player.x, my.x) > 50) {wait (1);}

When this happens, the got_key variable is set to 1, and the gotkey_wav sound is played at its maximum level (100%).

got_key = 1;

snd_play (gotkey_wav, 100, 0);

Now that the proper variable has been set to 1, it's time to get rid of the key model and action for good. We can't do that instantly, though, because the gotkey_wav sound, which was triggered by the action attached to the key, is still playing, and it would end abruptly. That's why we use the next two line of code, which make the key action wait for a second, and only after that remove the key model (and thus, its associated action) for good.

wait(-1);

ent_remove (my);

}

On a side note, we could also make the key model invisible (it was passable from the very beginning), but that would waste processor and memory resources, and we want to use them for the good game stuff ;)

The action below is attached to the lock/chest model. I have used a lock/chest combo because you can always make the lock a part of the chest and animate it either separately, or together with it.

action Chest ()

{

var init_pan;

init_pan = my.pan;

The chest has an initial pan angle value that has been set up by the level designer. So, the first two lines of code inside the "Chest" action store that initial pan value; we will use it later on.

while (!player) {wait (1);}

while (!got_key) {wait (1);}

The first line of code waits until the player is loaded in the level, and then, after the "player" pointer is valid, the code waits again, until the got_key variable is set to 1 (the key has been discovered and picked up).

while (vec_dist (player.x, my.x) > 100) {wait (1);}

It's time to wait once again, until the player comes close enough to the lock/chest. Play with "100"; it sets the distance that triggers the lock/chest unlocking, and this value is heavily dependent upon level geometry (size of the 3D model, etc).

snd_play (chestopens_wav, 100, 0);

while (my.pan > init_pan - 90)

{

c_rotate (me, vector(-4 * time_step, 0, 0), IGNORE_MAPS);

wait (1);

}

}

If the player has picked up the key and is close enough to the lock, the chestopens_wav sound will be played. The lock/chest will start to open by rotating around its hinge, showing the player the next clue.