Masquerading as a door

MissBubbles asked on func_ if it’s possible to create a func_door which inflicts damage when touched – as opposed to just when it is blocked by an entity. func_door is notoriously resistant to map hacks because its spawn function is extremely busy, which locks down lots of the traditional routes to creating a map hack. Challenge accepted!

The heart and soul of the door code is LinkDoors, which is intended to connect adjacent doors and make them open all at once. This sets a bunch of entity fields which are needed when a door is activated – even if this door doesn’t have any siblings to connect to. As readers of the last post on logic gates may recall, entity fields cannot be set from the map editor when performing a hack, because they depend on unpredictable entity numbers. So we need to find a way to incorporate this function into our plan.

On the other hand, this might be an opportunity rather than a limitation. We can create one entity to run LinkDoors for us, and another to actually be the solid part of the door. LinkDoors then lets us use the former entity to activate door movement on the latter entity. Our only restriction is that LinkDoors will only link entities with the same classname, so we need to develop a new entity hack idea I’ve christened Classname Masquerading.

The Activator

Our first entity is the one which we will trigger to move the door. Add a point entity to the map and give it a classname of SUB_CalcMoveDone. This function is handy in a number hacks, because it performs a small number of side effects, before immediately running whatever function is in the think1 field. Today’s idea is that you can put the spawn function you really want to run into think1 and get an entity of that class, but with a classname value of "SUB_CalcMoveDone". By varying the spawn function, we can create a variety of different entities, all masquerading as the same classname.

The only thing we need to get out of the activator’s spawn function is to have it run LinkDoors after a bit of a delay. Add the following keys:

"think1" "HuntTarget"
"th_run" "LinkDoors"

HuntTarget schedules the function stored in th_run to occur in 0.1 seconds. If we store LinkDoors there, then we get exactly what we need. Now add the follow two keys, which let us trigger the entity later in the map:

"use" "door_use"
"targetname" "spike"

You also need to add a button or trigger with a target of spike to activate it.

The Physical Door

Now we can add the brush entity for the door – note that this hack does depend on the brush entity being loaded after the activator entity, which usually means you must create them in the editor in this order. Give this brush entity the classname of SUB_CalcMoveDone as well – this is the crux of the masquerade idea. Give it a key of think1 func_wall, then quickly compile your map to check it has worked. If all went to plan, the brush entity should be visible and solid, exactly like a func_wall. The only difference should be that when you run the edicts command, you can see it still has a classname of SUB_CalcMoveDone.

Come back to the editor, and add the following keys to the brush entity

"speed" "100"
"wait" "4"
"dmg" "3"
"pos2" "0 0 -100"
"state" "1"
"touch" "door_blocked"

These are for the most part fields that would normally be set by the func_door spawn function, and you may need to adjust them to suit your map, e.g. my example value for pos2 moves the door 100 units vertically down when the door opens. You must specify the distance and direction you would like the door to move instead. Also of note is the touch function, which is non-standard, but is there to satisfy MissBubbles’ original request that the door inflict damage on any contact.

Linking Them Together

You may have tried to compile the map at this stage, and noticed it still doesn’t do anything! There’s one last thing to go. Identify the coordinates of a point which lies within the brush entity. In my test map, I chose the point -96 -96 184. You need to add two keys to the activator entity, both of which must contain this coordinate.

"mins" "-96 -96 184"
"maxs" "-96 -96 184"

This makes the LinkDoors function believe that the activator is adjacent to the brush entity, and so they need linking. The actual position of the activator is not relevant. Compile away at this point and the hack should work.

Final Remarks

There’s a little bit of tidying up to do still. You’ll notice errors about unprecached sounds. The way to fix these is to specify sounds for the brush entity in the keys noise1 and noise2. You will need these sounds to be precached by some other entity in your map. You may also need to add spawnflags and restore the door_blocked function to the brush entity – without using the func_door spawn function setting these things becomes your responsibility.

As often happens, a map hack developed to solve a specific problem has in fact opened up a wide vista of applications. Because we’ve built something that moves like a door, but is based off of a func_wall instead, we have the opportunity to modify it further. We can add custom blocked, th_pain and th_die functions on top of making a different choice for touch. func_wall already comes with a use function which we can’t override, but it’s quite an interesting one – it toggles any animated textures on the brush entity. Than’s idbase door textures could be put to use here.

The very brave might consider using something more complicated than a func_wall with this method, it might unlock a single entity that can move in two distinct ways. And the masquerade idea has many other uses, some of which will appear in a future article.

Test map: This is a quick demonstration of the bare minimum hack. The sounds for the spike are chosen from those automatically precached in every map, so they’re a bit weird. At least it demonstrates how freely they can be chosen…


Leave a Reply

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

You are commenting using your 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