The Factory Pattern

Today we’re going to look at another pattern from the wider world of programming, and see what we can do with it in Quake. As the title suggests, we’re going to use the Factory Pattern, probably made most famous by Java. We won’t try to define it up front though, we’ll just play around with a clean copy of the QuakeC source until we discover it, then look back on it when we’re done.

Start with doors.qc and immediately seek out func_door. Replace the entire spawn function with

void() func_door =
    self.solid = SOLID_BSP;
    self.movetype = MOVETYPE_PUSH;
    self.angles = '0 0 0';
    setmodel(self, self.model);
    setorigin (self, self.origin);    
    self.takedamage = DAMAGE_AIM; = 30;
    self.th_die = SUB_Remove;
    self.classname = "destructible";

Now every func_door entity has been replaced with a crude destructible entity (this is simpler than creating a properly named entity class and then needing a custom test map to go with it). Some maps are broken by this change if they use doors as lifts, but it’s fun to storm through going “gold key door, eh? Meet my double-barrelled locksmith!”. And while it’s a weird change to make, the code behind it is straightforward.

Time to try something stranger, make the following changes to the spawn function:

void() func_door =
    local entity instance;
    instance = spawn();
    instance.solid = SOLID_BSP;
    instance.movetype = MOVETYPE_PUSH;
    instance.angles = '0 0 0';
    setmodel(instance, self.model);
    setorigin (instance, self.origin);    
    instance.takedamage = DAMAGE_AIM; = 30;
    instance.th_die = SUB_Remove;
    instance.classname = "destructible";

The change here is that we’re spawning a new blank entity to be the destructible part. For most of the code this change is a simple substitution. Pay closest attention to setmodel(instance, self.model); – notice how we still look up the model on the self entity, but apply it to the new instance instead.

You might not notice any change to the game once we do this, but here’s a theoretical difference. Imagine that someone used the armour values maphack to create an extra tough func_door (anticipating our changes). On the original version of the code that would work, but in the new version the armour is only applied to the original entity, not the new one that we made visible. That means this hack cannot be applied – we’ve made an entity “immune” to hacking!

What has become of the original entity then? It’s still loaded, but just sits there inert – a glorified info_notnull with another classname. If all we wanted to do was prevent maphacks on this entity class (and that’s occasionally a good plan) then we might as well tag on remove(self); to the end of the spawn function and clear up the cruft. Not today though; we’re actually going to turn this entity into the star of the tutorial: The Factory.

If you tested this mod on e2m2 you might have encountered a difficulty just past the gold key door. Either through your own carelessness or the leaping attack of the fiend it’s quite possible to destroy the platform across the moat, leaving you stranded. To fix this we can make the destructible doors respawn after 5 seconds of being dead. Please gloss over the weirdness of this plan, not to mention the chances of things getting stuck! The doors will need a custom death function now:

void() destructible_die =
self.owner.nextthink = time + 5;
self.owner.think = func_door;

Notice that we’re making the factory re-run its spawn function! Only two changes are needed in the spawn function itself: change instance.th_die = destructible_die; to hook up the new code and add instance.owner = self; to tie the instance to her factory.

This demonstrates what a factory does – it’s an entity whose job is manufacturing other entities (instances). It’s worth taking a second to compare what we’ve made to two existing examples in the QuakeC. One point of comparison is respawning items in deathmatch, which manage to respawn without a separate factory entity – instead the code manually resets the required fields. However, all weapon_nailgun entities are essentially identical at spawn, and the only way they change is by being collected. Contrast this to something like a monster which turn, walk about, change ai state, take damage, have movetargets – by the time one has died it’s almost impossible to reset one to a baseline. Even our basic destructibles are harder to reset as they each have a unique model which would need to be remembered.

On the other hand, the spikeshooter entities are fully fledged factories to begin with. They’re a useful example of how a factory entity can also be very different in design to the instances it creates. The keys a mapper can set on a spikeshooter are things like setting the rate at which they fire and the delay before the initial shot – i.e. the keys control the manufacturing process rather than describe the instance being manufactured. Which isn’t to say that the template-style factory displayed in our example is misguided, it’s just an alternative.

Above I mentioned monsters, and in fact there are some examples of monster-spawning factories in Quoth, it’s a pretty natural example of the pattern in action. If you want to do so too, you need to do one thing to the pattern in the code here: separate the factory preparation code from the instance creation code. Specifically, all the things that need to be done in frame 1 like precaching models and setting the monster count should go in one function, and the actual monster creation stuff should go in another. In our toy example there are no precaches to be made, so the spawn function is nothing but the instance creation function.

Don’t dismiss the toy example as useless though. In a round-based mod styled after Counter-Strike, where the map needs to reset after each map, it’s just the ticket. You’d need to flesh the breakable class out a bit with some rubble and sounds, but it’s the cleanest way to get an identical entity each round when paired with some code that cleans up the last lot before the new round.


2 thoughts on “The Factory Pattern

  1. OMG – this stuff is amazing. I had no idea fteqcc could do some of these things.
    I just wrote the most amazing piece of code last night…
    I might have to change my moddb group name to qc++!
    Do you mind if I cross post (full credit will be given) this stuff there:

  2. Hi Six, your comment got flagged as spam and I just caught it now! Yes, please feel free to share it with the group.

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