As promised, this post will look at overlapping fields, following up on the naming tricks from yesterday. This trick does not rely on a quirk of naming fields in QuakeC, but it has a useful effect on how mappers can use named fields. In addition the trick is greatly improved by use of the hidden fields quirk. If you re-read yesterday’s explanation of vector compilation, from which we discovered hidden fields, you might notice some talk about overlapping. Today we intend to do the same with fields of our choosing.
To pull this off we actually need an compiler extension from FTEQCC. It lets us use the
var keyword to overlap two fields. For example, to create a field
bar which overlaps the field
foo we would write:
.float foo; var .float bar = foo;
You can make a quick mod which does this by adding the above code to defs.qc. You will then need a test map – create an
info_notnull in the map and set the
foo key on it equal to 12. If you then load the map, and run the “edicts” command in the console you should find that the
info_notnull lists both
foo 12.0 and
bar 12.0. Check that it works the other way round by deleting the
foo key and making a
bar key instead. You can also check that both fields receive the same values when set by writing some testing code in QuakeC. Now that we’ve seen that both fields overlap, we’d probably prefer to only see one appear in the eprint, so I’d recommend making any overlapping fields hidden as well, so just the original prints.
A quick note on what happens if a mapper sets both fields on one entity: the engine will overwrite one value with the other, but which you get may be inconsistant. For example, I’ve seen light tools reverse the order in which entity fields are written in the bsp file. This would lead to a different value being set in the lit and unlit versions of the map, so I don’t encourage it.
There are two reasons I can give for using overlapping fields. In QuakeC, coders will often let the same field represent two different concepts, depending on which class of entity they are on. For example you might store the rage level for a particular monster in the
.lip field (which traditionally is only used for a
func_plat). This kind of reuse is understandable given that all fields must be added to every entity, which can seem wasteful. One use of overlapping fields is to make this kind of reuse explicit in your code, while avoiding the confusion of inappropriate field names. If you add a definition…
var .float rage__ = lip;
…you can use the more descriptive name
rage__ in your monster code. Notice we have hidden this second field for the reason given above.
Of course, the pitfall still remains that as you reuse a field more often, the chance of accidentally using it for two purposes at once on a particular entity increases, overlapping fields can’t help you there. Also notice that as it stands mappers can still use either
rage__ to set the underlying field. If you only want one name available you can make the overlapping one private, or just use something like…
#DEFINE RAGE lip
…to create an “alias” for the
lip field without adding an extra public name.
Our second case for using overlapping fields is to intentionally give extra names to mappers, in particular descriptive names of generic things. For this use I’ll give an extended example.
Suppose that we wanted to redesign the trigger system in quake to be event/action based. Each class of map entities is to have several events defined, such as:
- button is pressed
- door has finished opening
- trigger is no longer occupied
and a set of actions:
- toggle door
- unlock button
- remove trigger
Each event and each action would have a string field associated, in the way
targetname work. When an event occurs, we search using the string in that entity’s event field for any actions which contain the same string, and run those actions.
The naive way of creating a new field for every different event and action would have some problems. It would not be a problem to find the correct event string when one occurs, as each class knows which fields it uses. However this string would then have to be compared with every single action field for all possible entity classes, which would grow into a monstrous function. Not to mention the people who wanted to reuse the lip field, who by now are cringing at how many extra fields this will take…
One way around this would be to only define very generic fields
//events .string event1 ... .string event4 //actions .string action1 ... .string action5
This helps because you only have to search
action5 for each entity on the map, and send entities a signal to invoke action ‘n’, using a class specific function. This has the benefit of short and tidy code which is less memory-hungry. Unfortunately the design is not mapper-friendly – the names are completely unmemorable and using any class would require constant reference to the documentation.
As you might have worked out by now, hidden overlapping fields let us keep this design in the back-end while presenting a sensible front-end to mappers. Each time you create a new class of entity you define friendly names which overlap the generic fields:
//door actions var .string DoorUnlock__ = action1; var .string DoorToggle__ = action2; var .string DoorOpen__ = action3; var .string DoorClose__ = action4; var .string DoorLock__ = action5; //door events var .string DoorTouched__ = event1; var .string DoorOpened__ = event2; var .string DoorClosed__ = event3; var .string DoorBlocked__ = event4;
(n.b.: there’s a design behind placing the unlock function first in the order. It seems inevitable that anyone writing this code would test actions in order from
action5. I expect someone might want to unlock and open the door at once by giving both actions the same name, but the door won’t open if it isn’t unlocked first. Can you see why
DoorLock__ might be last?)
Of course, you also gain the benefit that in your door code you can make use of the same descriptive names – everyone’s a winner! Except people with broken underscore keys…