A to Z Scripting: Practical exercise: subtle teleportation

From The DarkMod Wiki
Jump to navigationJump to search

So far we've seen the basics of scripting, including the basic composition of a script, how variables are made and used, how to "get" information about entities in the map, all the ways to call a script, how to use the TDM Script Reference as well as how to set up your .script file. That would already be enough to try an early scripting exercise:

Say you were making some kind of wizard's tower, or had 2 separate areas that should look like they're physically connected: you'd want to teleport the player between 2 rooms without him realising it. That means both rooms should look identical, and the player should be teleported to the exactly same position in the other room.


Before you start writing any lines of script, it's good to plan this out, maybe even writing down notes somewhere. The more you think of now, the fewer roadblocks there might be down the line:

  • 1) What will be teleported? Just the player, or maybe also moveables and even AIs?
  • 2) How will the script know where the player/etc. should be teleported?
  • 3) How should the rooms be made? Should AIs have access? Should there be extinguishable lamps? Where should the trigger brushes be?

1) What will be teleported? Just the player, or maybe also moveables and even AIs?

Since this is a basic script, it's better to keep things simple, so only the player will be teleported for now. Moveables would be more more advanced, since they would require some way of detecting them (i.e. with a room-filling trigger_touch brush) and a way of running the teleportation script on every one of them. AIs most likely shouldn't be teleported since it would disrupt their pathfinding.

2) How will the script know where the player should be teleported?

To maintain the illusion, we want the player to be teleported to the exact same position in the other room. That means we can't teleport him to a fixed position, but will instead have to get his current position and modify it with an offset.

Say the 2nd room was 1024 units off to the right compared to the first unit. In that case, you can simply add or subtract 1024 from the player's origin on the x axis to move him between the 2 rooms. But if you ever moved these rooms, you'd have to manually update this number.

Therefore it's better to automatically calculate the offset as a variable. All you need is to take the position of some kind of reference point in both rooms, such as the origin of a piece of furniture, and find the difference. This vector will always take you to the corresponding point in the other room.

3) How should the rooms be made? Should AIs have access? Should there be extinguishable lamps? Where should the trigger brushes be?

This is more of a mapping question. We want there to be as few differences as possible between the 2 rooms, so any non-static entities like AIs, loot, moveables or extinguishable lamps shouldn't be found in or near these rooms (unless you want to put in the extra work of synchronising the 2 rooms' copies via script). AIs should have no way of pathing into these rooms.

The next mapping question is layout. Corridors leading to/from the rooms could be L-shaped to minimise what the player can see when the transition happens (to reduce how much you have to keep identical). You will also need to place the trigger brushes so that the player doesn't teleport and immediately stumble into the brush that teleports him back where he came from.

The mapping

Next you will want to do the mapping in DR, as you'll need the entity names later on for the script. After the brainstorming, this is a setup that could work:

Practical exercise teleportation2.png

These are the key features of the mapping setup:

  • two trigger_multi brushes at opposite ends of the rooms. They're located such that the player only activates one on his path through the setup.
    • each brush targets a callscriptfunction entity to call the teleportation script. Callscriptfunction entities are more advanced than trigger brushes, since they can tell the script which trigger brush was activated (see A to Z Scripting: Ways of calling a script > "callscriptfunction").
    • each brush has a custom spawnarg called "direction" either with the value "forward" or "backward". The script looks up this spawnarg on the brush that called the script.
    • Checking which brush called the script allows us to reuse a single script, rather than writing one script that teleports forward and one almost identical script that teleports backward.
  • the two callscriptfunction entities are shown in the void for easier visibility, you'd place them inside the rooms or somewhere else that's sealed.
    • the "foreach" "1" spawnarg is what allows them to pass on to the script which brush triggered them.
  • the table in the centre of each room acts as the reference point for calculating the teleportation offset. For easier reference in the script, they've been given special names (table_1, table_2).
  • all 4 corridors are blind and L-shaped. The player enters the setup via the bottom left corridor and leaves via the top right corridor, so you'd attach the rest of your map to these 2 corridors.

The scripting

Now that all the planning and setup are out of the way, the scripting itself can begin. Start with thinking about what important components are involved: one script, and one variable for storing the teleportation offset. Once you've determined what the teleportation offset should be, it's simply a matter of applying it to the player's current position.

void teleport_script(entity ent_target, entity ent_brush, entity ent_callscriptfunction)	//receive up to 3 entities as input from the callscriptfunction entity. Only ent_brush is used in this script
	vector offset = $table_2.getOrigin() - $table_1.getOrigin();		//calculate the vector to get to room 2 from room 1, using the tables as reference points

	if (ent_brush.getKey("direction") == "backwards") offset = -offset;	//check the "direction" spawnarg on the brush that called the script. If "backwards", make the offset become negative

	$player1.setOrigin($player1.getOrigin() + offset);			//teleport the player by taking his current position and adding the teleportation offset to it

The setup is now done. If everything is done right, the player would now teleport seamlessly between the 2 rooms without ever noticing. You could place some kind of unique item in one of the rooms (i.e. a big torch) to demonstrate this more visibly.

Looking back

A faster and simpler method would've been to just make one script for each brush: one for teleporting forwards, and one for teleporting backwards. However, these scripts would've basically been duplicates except for a + or - sign. It's best to get into the habit of writing a piece of code only once and using variables in order to change how it plays out.

Further comments:

  • A lot of the work in this case went into the planning and mapping phases, while the scripting itself was quite short (possibly thanks in part to the advance planning). Planning setups like this should become easier the more you've done with scripting in your maps.
  • Technically one callscriptfunction entity would've been enough, rather than one for each trigger brush. However, this might look a bit messy if the 2 rooms are far apart. It looks neater in my opinion if you don't have target lines crisscrossing the map.
  • Since there are 2 callscriptfunction entities, you could also have stored the "direction" spawnargs on them instead of on the brushes.

Next / previous article