Logic gates for 2017

Logic gates go back a long way in Quake, starting with metlslime’s tutorial for Quake 2 all the way back in 1998. I first came across them on qmap, the now lost predecessor of func_msgboard. In 2013 I wrote a tutorial on this blog on how to create a logic gate using entity hacks, to take some of the computer-made-from-dominoes out of them. Then in 2016 I edited that post to fix some bugs, and it turns out the fix I wrote was over-complex in some ways, and still deficient in other circumstances.

Given that, I’ve decided to write up my latest design for logic gates in a stand-alone article. Apart from providing a single place for new readers to get the design from, I hope that the simplification in the new method and the extra features it offers will be helpful for everyone.

What is a logic gate?

A logic gate is an arrangement of entities with a single input and multiple possible outputs. An output can be selected by triggering it. When the input is triggered, the target of the selected output is triggered. In the simplest version of a logic gate, there are just two outputs, and only one of them has a target. This is in effect an on-off switch – select the output with the target to turn it on, and select the output without one to turn it off again. This gives you control over the logic of triggers in your map, for example creating a button which only opens the door if the generator is running, or only triggering an ambush when the player is on the way back from collecting the gold key.

In this article, I have certain aims for the gate that we are building. The gate we design will:

  • switch between outputs instantly
  • relay input to the selected target instantly
  • support multiple inputs to the gate on a single frame
  • use as few entities as possible
  • have no limit to the number of times it can be activated

Plan of action

The logic gate we will build today revolves around interactions that inflict damage on an enemy entity, rather than the usual targettargetname pairs. Each entity has their own enemy field, and there are lots of functions in the QuakeC which alter which entity is stored there (mostly relating to monster ai). Being easy to change makes it a suitable place to store the selected output. The downside is that enemy is stored as an entity number, which we cannot specify directly. This means instead of just linking the entities in advance from the map editor, we make them perform a sequence of attacks on each other, which will leave them connected correctly.

We will use one entity for each of the outputs, and a further one for the input. The enemy of the input is the most important part of the arrangement, this will contain one of the output entities, which determines which output is selected. All of the output entities will have the input as their enemy, but this is purely to make it easier for them to attack it when they need to change the input’s enemy. It’s worth noting that all the business with enemy is internal to the logic gate – communication with the wider map is done in the usual way with target and targetname.

The entities in the gate need to be placed in close proximity to each other for the attacks they use to succeed, and they cannot be placed in a part of the map where any other entity (including the player, or other logic gates) can interact with them. As point entities, they will cause your map to leak if you leave them in the void. The thing to do is build a 64x64x64 unit box outside of the proper map for each logic gate you wish to build, in the same way that you build boxes for monsters to teleport from.

Setting the scene

Because the hack is quite complicated, the article will build up the hack in stages, and at the start of each stage I will outline what we expect the entities to do at the end. Then I will describe what keys to add to the entities you have in your map. If you are building along at home, you should check that your set-up is doing what I claim it should at the end of each section. To kick things off, create the following five vanilla entities in your map:

  • a trigger_multiple with a target of gate_input
  • a trap_spikeshooter with a targetname of gate_output_a
  • a shootable func_button alongside the shooter with a target of gate_select_a
  • a second trap_spikeshooter with a targetname of gate_output_b
  • a second shootable button with a target of gate_select_b

When the hack is complete, the shootable buttons will allow you to toggle which trap_spikeshooter activates when you enter the trigger. At the moment, none of the entities will do anything.

Do I have your attention?

Even spreading the parts through stages, we need to front-load a lots of the keys into the first stage, otherwise things don’t even load at all. In a couple of cases, we need to specify something for a key or the game crashes, but the actual choice we make doesn’t pay off until a later stage. Our objective for this stage is to reach the point where the input entity is recorded as the enemy of both of the outputs.

Create a 64x64x64 box outside your map, and place a point entity in the centre. This will be the input entity, give it the following keys:

{
"classname" "info_notnull"
"targetname" "gate_input"
"think" "W_FireLightning" // fire like the player using the lightning gun
"nextthink" "0.05" // on the first frame of the server
"waterlevel" "2" // fake being underwater so that the gun explodes
"ammo_cells" "10" // supply enough ammo to make the explosion large
}

The input will set off an explosion in the box on the first frame of the map. The idea is that this blast will hit the outputs, and make itself their enemy as a result. Now add a second point entity to the box with these keys:

 {
"classname" "SUB_Null" // enemies need to have different classnames
"targetname" "gate_select_a"
"takedamage" "1" // let the entity take damage
"health" "9999" // enough health to survive the explosion
"solid" "1" // non-solid entities are not hit by radius attacks
"flags" "32" // pretend to be a monster so that enemy is set
"th_run" "multi_wait" // without a run function Quake will crash
}

This entity is not an info_notnull! Monsters of the same type (meaning same classname) won’t infight, so we need a different spawn function. SUB_Null is chosen because it does nothing, just like info_notnull. This entity will be the first output, but we need a second. Create a copy next to it, and change the targetname to gate_select_b. Load the map, and use the “edicts” console command to check that both of the outputs have an enemy key.

And With Strange Aeons…

Our next milestone is to make it so that both of the outputs come away from the explosion with -1 health, so that any further damage they take will kill them. I commented above that we needed to choose something for the th_run function on the outputs, but the choice I made was deliberate. Once we set a single key on the entities, multi_wait will do the rest.

 
"max_health" "-1" 

When multi_wait runs, it sets the health of the entity back to the value of max_health, even if that value is less than the health the entity currently has. Use “edicts” again to check the health value of the outputs, and we’re done.

On the Counter-Attack

Because the outputs each have the input entity as their enemy, we can give them the chance to fight back. By the end of this stage, we will be able to toggle the enemy value of the input, pointing it at either of the outputs, using the shootable buttons we created in the initial set-up. Again it’s just a single key to add to each output:

 
"use" "ai_melee" // perform a melee attack on enemy

Despite the “ai” prefix this function is not about thinking, just inflicting damage if enemy is in range. We also need to modify the input entity so that it will take damage and recognise a new enemy, add these keys:

 
{
"takedamage" "1" // allow attacks to injure this entity
"health" "-1" // any attack will kill the entity
"th_die" "multi_wait" // when killed, the above fields gets reset
"movetype" "8" // makes sure enemy field is set by the attack
"max_health" "-1" //when reset, health is still negative
}

Test out the shootable buttons, each time using the edicts command to check that the input’s enemy field contains the entity number of the appropriate output.

Cycle of Violence

Now that we can point the input at either of the outputs, we need to give the input a new attack which only hits the selected output. At the same time, we need to add the response function to the output, or the game will crash. Once we’ve done this, the gate is complete. Add the following key to the input entity:

 
"use" "ai_melee"

Also add the following key to the output with targetname gate_select_a:

 
"th_die" "SUB_UseTargets" // when killed, trigger the target
"target" "gate_output_a" // specify the target

Finally add this to the other output:

 
"th_die" "SUB_UseTargets" // when killed, trigger the target
"target" "gate_output_b" // specify the target

To test, shoot the wall buttons, and check that they change which spikeshooter is activated by standing in the trigger_multiple.

Starting Configuration

You may have spotted in the previous test that before you shoot the buttons, the trigger_multiple doesn’t activate either of the spikeshooters. This is because the input doesn’t start with an enemy, and it remains unset until the first output is triggered. We can speed this up by adding the following keys to one of the outputs:

 
"th_pain" "ai_melee"

The pain function will be triggered by the initial explosion, launching an immediate counter-attack from this output against the input entity, exactly as if something had triggered this output.

Variant: on-off switch

The most common thing you want to do with a logic gate is have a way to turn a trigger on or off at will. The way to do this is to create an output which leads nowhere. Deleting the target on one of the outputs is enough to do that. It’s a bit more efficient to change th_die to SUB_Null after you do that. What you can’t afford to do is completely remove the th_die function, Quake will crash if you do that!


This post can’t stand to grow any longer. In a follow-up post I’ll describe how to assemble multiple logic gates into the OR Gate, the AND Gate, and the XOR gate. But before I finish I would like to highlight two particular features of this setup. One is that you can add any number of additional outputs with their own targetname and target to the logic gate box. All of them will become new possible targets for the input, with no other changes needed!

The other nice feature is that it’s extremely portable. If you need to make a second logic gate, select the first one and paste a copy alongside. You may need to change the targetname and target keys, because these are the values that relate to the other entities of your map. The nice part is that you don’t have to do anything else, there’s no targetname internal to the gate because it all works through proximity. In fact, you can even copy-paste it straight out of the test map.

Oh yeah, the test map. The map comes with a readme which explains how the parts of the map test all the features I promised the gate would deliver. It’s a fullbright box map in Egyptian textures. Enjoy!

Advertisements

2 thoughts on “Logic gates for 2017

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s