This tutorial will guide you through the process of setting up a toner puzzle in Half-Life: Alyx. "Toner" is the in-editor name for wire tracing puzzles. These puzzles require a lot of entities and thus can be slow to set up, but they offer some nice opportunities for unique gameplay and can help provide some down-time in your map in a small amount of space.
There are four entities you will need to worry about:
- info_hlvr_toner_port: The hole in the wall where a toner puzzle begins.
- info_hlvr_toner_junction: Nodes along the toner path that the player can interact with by rotating them.
- info_hlvr_toner_path: Paths that the player traces with the multi-tool. You need only one of this entity for a single path.
- info_hlvr_toner_path_node: Individual points that define the shape of a info_hlvr_toner_path.
Before you begin, you may want to consider placing your toner puzzle inside of a Prefab. This will make it easier to edit the puzzle on its own without accidentally clicking on other entities in the map, and is generally a clean way to work. You can do this by placing the first entity, then right clicking and choosing Selected objects > New Prefab from Selection (Maintain World Offset).
To begin making your Toner puzzle, you will want to create an info_hlvr_toner_port. There are a few keyvalues you will want to pay attention to:
- Start Disabled: Should the port start disabled? If the port is disabled, it will unusable in-game. If the puzzle should start usable, don't check this.
- Start Port Visible: Should the interactive element underneath the port be shown when the player uses their multi-tool? If this puzzle should start usable, check this.
- Start Visible: Should the entire Toner puzzle attached to this port be shown when the player uses their multi-tool? If this puzzle should start usable, check this.
- Initial Orientation, Desired Orientation: This is the angle that the interactive element under the port will rotate to start the puzzle. Desired Orientation should always be 90 degrees larger than Initial Orientation, or else the port will fail to work properly.
For the purpose of this example, I will be calling this port toner1_port.
After placing your port entity, begin placing your info_hlvr_toner_junction entities. Each junction should have a unique name, as we will need to reference each one later on when we are adding the paths. The orientation of this entity matters, as the actual position of the junction will be inset a number of units into the wall, defined by the Inset Distance keyvalue.
You can also change what kind of junction this entity is with the Topology keyvalue. This also includes static junctions, where the signal splits into multiple paths but the player cannot interact. Also note that rotating the entity in plane with the junction will not actually rotate the junction; use the Orientation keyvalue to change how the junction starts rotated.
For the purpose of this example, I will be calling each junction toner1_j#, where # is a unique number for each junction.
Placing Path Nodes
First, select the junction where you want to begin the path, and use info_hlvr_toner_path_node. This will help ensure that the junction and the path node are in-plane with each other, as well as preserving the Inset Distance value between the two entities. You may want to delete the unused keyvalues (marked with a red X at the bottom of the keyvalue list) for cleanliness. Also, give this node a new name. For this example, I will be using toner1_p#_n1, where # is a unique number for each path.+ (drag) (or + ) to duplicate the entity, then change the new entity to a
When you create a new node, the editor will display a red symbol around the node. This indicates that the node is not yet associated with any path. You can ignore this for now.
One you have the first node in the path, you can then continue shift-dragging new nodes to extend the path. Keep doing this until you've drawn your path from one junction (or port) to the next. If you've been following the naming scheme in this example, the editor will automatically rename each node like n2, n3, n4, etc. Also note that you do not need to have the beginning or ending nodes actually touching the junction (or port), as the engine will link that up for us later.
Note that each node has a Spline node keyvalue, which toggles whether or not the path will be a straight line or act as a curve through this point. Spline nodes are useful for natural curves, while non-spline nodes are useful for tracing more exact shapes (like if a wire is to be precisely within a small pipe).
Repeat this process for every path in your puzzle. (This can be tedious.)
Placing Path Entities
Once we have all of our junctions and path nodes set up, we can start setting up the actual path entities. This entity handles all of the actual entity logic work. You'll need one info_hlvr_toner_path per path in your puzzle, each with a unique name. For this example, I will be using toner1_p#, where the # matches the number used in the path nodes for this path.
Here are the keyvalues you will need to pay attention to:
- First Path Node: The first info_hlvr_toner_path_node in the path. With our naming scheme, this will always be the entity with a name ending in "n1".
- Start: The junction or port that this path starts at. This will connect to the "n1" node. This is required.
- End: The junction or port that this path connects to. This will connect to the last node in the path. This is not required.
When assigning all of these entity names, you will want to take advantage of the eyedropper icon to the right of each keyvalue, allowing you to select an entity in the 3D viewport to assign the target. This will save you a lot of work!
After we have created all of the paths with the nodes, starts, and ends (as applicable) all assigned, we aren't quite done yet. While we have told each path what junctions are connected to it, we also need to tell each junction which paths are connected to it.
In the keyvalues, you will see four Connected Path Entity keyvalues:
- 0: Right
- 1: Top
- 2: Left
- 3: Bottom
Each one of these needs to be associated with the correct info_hlvr_toner_path. The eyedropper tool will once again be your friend here. Also, the 2D and 3D viewports will display 0 1 2 3 on the edges of the currently selected junction to give an easy guide for which side is the right, top, left, or bottom.
Once you have repeated this process on every junction in the puzzle, congratulations! You have a fully functional toner puzzle that does not actually do anything yet.
Inputs / Outputs
Making your puzzle actually do something in the game world is simple. Each info_hlvr_toner_path has OnPowered and OnUnpowered outputs. Using these, you can have any path be linked to a door, gate, light, power plug, alarm, etc. and trigger it as you would with anything else through the I/O system.
In most cases, you will also want to make sure to mark the puzzle as complete when the final bit of path is powered on. To do this, send an input of SetComplete 1 to the port of the puzzle. This will also hide the puzzle from the player's view.
For ports that should start disabled, tick Start Disabled and untick Start Port Visible and Start Visible. To enable the port, fire inputs Enable and SetPortVisible 1.
Some Valve maps will use SetVisible on a port to toggle visibility on a puzzle based on a trigger so that the puzzle is only visible when the player is in the same room. This seems to be used in situations where the wiring may be visible through the other side of a wall, which may be confusing to the player.
"LL" junctions can be used to pass a signal through both L's of the junction, which can be used for some interesting signal puzzles. However, as of time of writing, there is a visual bug where only one half of the junction will light up.
The player's multi-tool has a generous range for locking onto a junction, and as a result, it can sometimes rotate the wrong one if two junctions are placed too closely together. 16 units seems to be about the minimal distance you should place two junctions apart.