Unblock chains - Part 2

How to set up even more complex chains of events

Chain of events

In a former issue we had examined how to set up things so that a single action may trigger a complex chain of subsequent events. Now we'll take the problem from the other side and take a closer look at multi-criteria triggers, that is triggers waiting for several conditions to be met before actually doing something.

It allows for interesting puzzles requiring a couple of actions from the player, which spices up the experience compared to the usual press-a-button-and-that's-it situation.

Once again, the best advice I can tell is: play Hexen II again, notice the nice puzzles there you like, and study how they were made after having decompiled Raven's maps thanks to Bsp2mph2.

Trigger_counter

A counter is obviously the easiest and simplest option. Give it a count, get a matching number of buttons or whatever pointing to it and you'll get that nice good old satisfactory sequence of "Only N more to go...", etc., "Sequence completed!" (unless you checked its "No message" spawflags, then it does its job silently). Simply don't make the newbie mistake and you're done.

Newbie mistake Trigger_counter counts how many time it was fired before triggering its own targets, but it doesn't check WHAT actually fired it: it might be same activator each time! So if the sequence is supposed to be 3 buttons pressed, make sure each button is "wait -1". Otherwise a single button may be pressed again and again and again until the sequence is completed and even beyond, and the 2 other buttons would be forever useless. That wouldn't break the map but make it feel buggy and awkward.

Okay, that was the trigger_counter's basic and most popular use. But it's far from all what the entity can do! Let's dive deeper...
* Law and order *
The most advanced uses revolve around order: not only trigger_counter waits for being fired N times, but it must be fired in a specific order (like button #1 first, then button #2 then button #3, and not another way around). For the sake of illustration, let's stick to that example of 3 buttons in the following (but of course, any variation would work as well).
  1. Check the trigger's "Ordered" spawnflags.
  2. Give each firing button an order number by setting its aflag property: 1, 2, 3.
  3. Give them all (trigger + buttons) the same netname.
  4. Make the trigger_counter's count the number of firing buttons minus 1. Yeah, that's weird and required for ordered trigger_counters only. Don't ask why.
* Tricky order *
It's possible to require the order to be fancier than just 1, 2, 3. What if the desired order would be 3, 1, 2 instead? Then there are 2 ways:
  • Just switch the aflag values between the func_button entities. Technically easy but might make the map a bit less legible for yourself since, if the buttons are in a row, the visual and logical order don't match anymore.
  • Leave the aflag values as they are and set the trigger's mangle property to the desired order. Since mangle is a vector, it must be set using the usual vector syntax, that is '3 1 2' for the example above. Obviously this technique is not intended to be used with triggers needing to be fired more than 3 times.

Ordered counter in Pyramid of Anubis
In in egypt5 - Pyramid of Anubis, this pharaoh bas-relief and the panels besides are buttons pointing to a trigger_counter opening the trapdoor below, with a non-matching visual and logical order.
As a side note, appreciate the beauty of how the light comes down from somewhere up right through the hole in the ceiling in perfect harmony with how the shadows were drawn on the bas-relief's 2D texture.

* Trickier order *
The mangle technique described above has a good side, although being applicable only to trigger_counter entities with count ⩽ 2 (yeah, remember the count-minus-1 oddity about ordered counters): it can be set dynamically at run time by a trigger_combination_assign targeting the counter.
So we can imagine a room containing 3 buttons and 3 archways leading there, with an invisible trigger_combination_assign across each archway. If the player goes through the first archway, the right order for the buttons to trigger the counter will be 1, 2, 3. Through the second archway it will be 2, 3, 1, and through the third one 3, 1, 2.

If the counter may be triggered more than once or if the player plays the map again in the future, this relative unpredictability and semi-randomness of the required order may add some variation and a bit of replayability (the player's memories from a previous time won't help, the puzzle has become fresh again).

Note that trigger_combination_assign changes the required order for its trigger_counter target but doesn't fire it.
* Return *
Just a short note about returning to an initial state:
  • If the trigger_counter's "Always return" spawnflags is set, the firing buttons will pop back to ready position even if the sequence was successfully completed (by default they stay pressed). No need to say it makes sense only if the counter is fired by buttons. In the vanilla game this feature is actually only used for the eye buttons of the most hated puzzle in Thysis.
  • An entity called trigger_counter_reset also exists which, as the comments in the vanilla HexenC code state, will reset a trigger_counter to start counting again as if it hasn't been used yet. Useful for when you want a counter to count more than once but the counting can be interrupted. Well, once again the only occurrence throughout the whole game is that eyes chessboard puzzle.

Egyptian eyes puzzle
Degorram knows which puzzle I'm referring to!

The f*** puzzle in question being known as buggy as hell and responsible for tons of PTSD among the Hexen II community, I would personally stay away those "refinements"... just in case.
* Baths of Demetrius: the Grand Game *
romeric4 - Baths of Demetrius is definitely one of my favorite maps in Hexen II: it's a relatively standalone map with its own specific stakes (the Crown of Kings and the Diamond Scepter needed in romeric7 - Reflecting Pool to access romeric5 - Temple of Mars), its starting area gives it a very refined atmosphere compared to the other mostly all-grey levels of the hub, and its puzzles are memorable and interesting.

One of those puzzles consists in pressing elemental buttons in the right order to reveal the Diamond Scepter. Failing to find the right combination leads to a skull wizard teleporting in to punish the player. This puzzle is a brilliant showcase of the most advanced use of a trigger_counter, and the occasion of talking about a few remaining lesser-known trigger_counter properties.
  • As an ordered counter (spawnflags 2), its count property is of course 3 for 4 buttons and all 5 share the same netname.
  • It displays a message as well in case of success (message property) or failure (msg2 property).
  • Its puzzle_id property contains the name of a target to fire in case of failure (namely a func_monsterspawner).
  • The firing buttons are ordered thanks to aflag and are "wait -1".
  • And the icing on the cake: if the pattern is incorrect, the buttons are automatically returned by the counter to unpressed state, ready for a new try!

Baths of Demetrius' master puzzles
trigger_counter upstairs, trigger_check downstairs.

Trigger_check

We can imagine trigger_check as a loose kind of trigger_counter, working a bit differently. We have a set of firing entities targeting the trigger, and everybody shares the same netname, like with ordered counters. But there stops the similarity; this time:
  • No need to set any count property in advance: the trigger figures out its firing entities by itself thanks to the shared netname.
  • Since strictly speaking it's not a question of counting anymore, don't be surprised there's often just 1 firing entity for 1 trigger_check. There's something important about that, cf. the very end of this page.
To be taken into account, each firing entity must be "OK" (whatever it means) and only when all firing entities are OK the trigger_check fires its own targets. "Being OK" is defined on a per-entity-type basis, so let's see the examples provided by Raven in their maps...
* func_pressure *
A func_pressure is a fancy variety of button who has a mass property. It becomes OK only when the total mass of the stuff lying on it is greater or equal to its mass property (it may take several objects to reach the required mass).

The vault in Pyramid of Anubis

The only 2 occurrences found in vanilla Hexen II are in a vault in egypt5 - Pyramid of Anubis and they happen to work in pair, both targeting the same trigger_check (all three sharing the same netname, as required). As expected, the trigger waits for both plates to carry the required mass to open the way.
* func_angletrigger *
A func_angletrigger is a sort of rotating button able to rotate by cnt degrees each move. Usually the player has to fire it several times to get it eventually pointing to the required absolute angle relative to the world defined by mangle. It's only then that the entity gets OK.
Most uses of trigger_check by Raven involve one or more func_angletrigger entities:
  • In egypt6 - Temple of Light, the sundial needed to point to a certain time is a textbook case.
  • Both wheels of ages in egypt2 - Ancient Temple of Nefertum and egypt3 - Temple of Nefertum are similar settings at a bigger scale, each needing to be aligned on a specific constellation (actually angle). As a side note, the puzzle features an additional layer of verification with a trigger_crosslevel/trigger_crosslevel_target pair to ensure that both ancient and current wheel are correctly aligned to spawn the crown of Lower Egypt (which is mistakenly referred to as "Upper Crown of Egypt" throughout the whole game, and vice versa: obviously no Egyptologist worked at Raven by then).
  • romeric4 - Baths of Demetrius offers a nice showcase here again with 3 func_angletriggers at the lower floor of the master trigger_counter puzzle. Only when all the red arrows each point to the right direction is the button freeing the barrels enabled.
* Other triggers *
Classic triggers are eligible to becoming "OK" and firing a trigger_check as well. The code in triggers.hc is a bit messy, but it doesn't seem to take more than firing a trigger to make it OK.

Fancy trapdoor in The False Temple
The only example by Raven is featured in tibet3 - The False Temple with a trigger_multiple where the Staff of Emperor Lo Pan takes part to a fancy trapdoor opening.

Unlike the pretty straightforward occurrences with func_pressure and func_angletrigger, settings with triggers as firing entities for a trigger_check would probably need a trial-and-error approach as the HexenC code is not so clear and the only available example is just a small part of a larger and fairly complex setting, which doesn't help the understanding.
* A final word *
No matter the kind of firing entity used, as far as I can tell all those listed above share the ability of possibly firing targets even while not being "OK". And since they don't necessarily take their own OK-itude into account to do their stuff, whenever the said OK-itude is important then a distinct dedicated entity is needed. Hence the point of trigger_check, even when there is only 1 firing entity involved. Firing targets and being OK are definitely two related but different things, and checking who is OK is trigger_check's business.
Even with that in mind, it's a quite confusing distinction which could easily lead to unexpected/messy situations, so my final advice would be: when an entity points to a trigger_check, unless you're really sure of what you're doing, better make sure the trigger_check is the only entity it points to. That's how Raven did each time, by the way. Might be a clue...

Want to ask for clarification, report an issue with this trick or propose another one? Drop me an email If you use the trick please credit me and put a link to this website.