Always a bigger fish

if you’re fighting a shambler and someone casts Summon Bigger Fish, how are you supposed to do collision?

for reference,

vector    VEC_HULL_MIN = '-16 -16 -24';
vector    VEC_HULL_MAX = '16 16 32';

vector    VEC_HULL2_MIN = '-32 -32 -24';
vector    VEC_HULL2_MAX = '32 32 64';

the first set of vectors is for hull1.  these are your small monsters like knights and zombies.  hull2, the second set of vectors is your ogres and shamblers.

if left to it’s own devices, quake has a funny way of handling collision for bounding boxes (hence forth, bbox) that have dimensions larger than the standard HULL2 size.

if for example, you created a new size:

vector    VEC_HULL3_MIN = '-128 -128 -24';
vector    VEC_HULL3_MAX = '128 128 64';

when quake is deciding where this new large monster will collide with the world, it will first default to hull2 in the bsp (i can only assume that somewhere in the source code, there’s an “if size is small, else just use hull2” somewhere).  but it will only collide against the first VEC_HULL2 units starting at your VEC_HULL3_MIN.

that is to say, only the coordinates from -128 -128 -24 to -64 -64 64.  this means that in some cases, your VEC_HULL3 sized monster will appear to be colliding correctly with the world, and at other times it will dip half way into the wall.

so how to get around this?

we know that no matter what bbox size, one monster will never walk inside another one.  the shambler on shub niggurath’s island in end.bsp can’t walk through shub herself even though she has a gigantic bbox.

so what are the properties needed to make use of this?  from testing, it seems as long as an entities .solid is SOLID_BBOX, this is enough.

NOTE that it MUST be a SOLID_BBOX, and not a SOLID_BSP, because a SOLID_BSP, while an entity, still uses HULL1/2 collision.

so what does this really mean?  essentially, we have to ‘trace’ the walls of any area we want to have larger bbox monsters moving around in with box entities.

obviously though, that’s not enough.  if we left it that way, the player would bump into those box entities as well as other monsters.

so, we shall have to do some hackery to get around that.

first we should create our collision entities.

void () func_clipModel_on =
	if (self.state == 0 ) //off, turn on
		self.state = 1;
		setorigin( self, self.origin - '8000 8000 8000' );

void () func_clipModel_off =
	if (self.state == 1 ) //on, turn off
		self.state = 0;
		setorigin( self, self.origin + '8000 8000 8000' );

void() func_clipModel =
	self.movetype = MOVETYPE_FLY;
	setmodel (self, self.model);
	setsize (self, self.mins, self.maxs);
	setorigin (self, self.origin);

	self.solid = SOLID_BBOX;
	self.model = string_null;

	self.state = 0; //always initially off.
	setorigin( self, self.origin + '8000 8000 8000' );


you may recognize this is originally how hipnotic created their func_togglewalls.

the main differences here are they work in reverse (are always initially off) and are not SOLID_BSP.  if you care, you could also modify this code so you don’t need to use brush models but just specify a mangle but that’s outside the scope of this post.

note that MOVETYPE_FLY is used.  to be honest, i’m not sure why it’s not MOVETYPE_NONE, and i can’t think of why it can’t be that.  just don’t use MOVETYPE_STEP, TOSS, BOUNCE… or it will fall to the ground.

there’s also two functions that were added, func_clipModel_on and func_clipModel_off.  i used the same method hipnotic did to toggle the walls on and off because i didn’t care to find a better way.  it works and there’s no need to change it.  note that these functions don’t just toggle.

ok, so that fluff is out of the way.  here’s the actual interesting stuff.

what you want to do is co-opt your movetogoal and walkmove functions so that these func_clipModels are only solid while you’re performing the built in function, and non-solid the rest of the time.

but you don’t want these to be solid for EVERY movetogoal call, since the small monsters don’t need it.

i opted for two things.

1.  only large monsters should use the special code

2. each large monster should only use it’s own set of func_clipModels and not other large monster’s set.

i tie a monster to it’s clipModels via ‘clipModel’->’targetname’ fields first.

next, all monsters use a wrapper movement function that passes the actual move function (ai_run, ai_charge, etc).

keep in mind, however, that this technique can easily be used for monster-only clip.


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 )

Google+ photo

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


Connecting to %s

%d bloggers like this: