Selective clipping

Today we’re going to create a pair of entity classes; one of which will work like a brush that only collides against players, and its opposite which collides with everything but players. However, these entities work best when they are comprised of clipping brushes, and there’s a quirk with clip in brush entities we must work around first.

If you create a func_wall and texture it all over with clip, you would be disappointed to find it doesn’t work in standard engines – you never collide with it. The issue is that clipping brushes do not give an entity its dimensions, and our entity contains nothing else, so it ends up zero-sized and gets ignored. Our workaround today is to sandwich a clipping brush between two thin conventional brushes. These outer brushes give the entity its dimensions, and the clipping brushes within the bounds start colliding (see below for illustration). You can bury the regular brushes in the world geometry.

clip-sandwich

On to the code! We start with a set of functions which make things solid and non-solid respectively to add to client.qc.

void(string target_class) MakeClassSolid =
{
	entity e;
	e = world;
	while( (e = find(e, classname, target_class)) )
	{
		e.solid = SOLID_BSP;
		e.movetype = MOVETYPE_PUSH;
		setmodel(e, e.mdl);
		e.model = string_null;
	}
};

void(string target_class) MakeClassNonSolid =
{
	entity e;
	e = world;
	while( (e = find(e, classname, target_class)) )
	{
		e.solid = SOLID_NOT;
		e.movetype = MOVETYPE_NONE;
		setmodel(e, "");
	}
};

The highlighted line above ensures that the entity is invisible while solid. We want to call these functions using the PlayerPreThink and PlayerPostThink functions to make func_player_clip solid during the player’s physics then non-solid afterwards, and vice-versa for func_nonplayer_clip.

Add the following to the very top of PlayerPreThink

	MakeClassSolid("func_player_clip");
	MakeClassNonSolid("func_nonplayer_clip");

Add this one to the very top of PlayerPostThink

	MakeClassNonSolid("func_player_clip");
	MakeClassSolid("func_nonplayer_clip");

All which remains is to add the spawn functions for our new entities. They are essentially just initialisation; the entities themselves don’t generate any think or touch events to function, as they are driven entirely by the code in the player functions.

void() func_nonplayer_clip =
{
	self.mdl = self.model;
	self.solid = SOLID_BSP;
	self.movetype = MOVETYPE_PUSH;
	setmodel(self, self.model);
	self.modelindex = 0;
};

void() func_player_clip =
{
	self.mdl = self.model;
};

Aside: Zero Player Game

Why do we make the func_nonplayer_clip entity solid, but not its opposite number? Well, imagine that we run a co-op server but no players have connected yet. Then PlayerPostThink never runs, but we still want the func_nonplayer_clip to be solid for the sake of patrolling monsters etc. It’s a good question to pose of any code you write relating to the player – what happens when there are no players? It forces you to avoid the assumption that The Player is a single entity, and so benefits co-op in general.


And now we’re done. Download clip.zip for a simple test map. In the first room you will encounter knights, and the blue tiles on the floor mark where the func_nonplayer_clip pillars are placed. One set of blue tiles are different; this pillar is made of regular brushes instead of clip brushes, can you tell the difference it makes? In the following room, you must navigate a maze of func_player_clip to reach your prize, but beware the zombies who can pass through the walls.

Important message: This article originally had fancier code with optimisation. Spike showed me that these optimisations were occasionally dangerous in every engine, rising to frequently incorrect in certain engines. The post has been edited to remove these optimisations.

Advertisements

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