Directions, please! (Part 2)

In part 1 we discussed all the various options I had considered for creating a “look at” trigger. This is the first of those options. A brush that triggered once a player looks a specific mangle:

/////////////////////
// On Look Direction
/////////////////////
void () lookdir_once_touch =
{
	float p_dir_r;
	float p_dir_l;
	float p_dir_f;
	float p_dir_b;
	float p_dir_u;
	float p_dir_d;
	
	makevectors(other.angles);
	vector o_forward = v_forward;
	vector o_up = v_up;
	vector o_right = v_right;
	
	// Forward and backward
	if(v_forward_x > self.speed) {
		p_dir_f = 1;
		p_dir_b = 0;
	}
	else if(v_forward_x < -self.speed) {
		p_dir_f = 0;
		p_dir_b = 1;
	} else {
		p_dir_f = 0;
		p_dir_b = 0;
	}
	
	// Left and Right
	if(v_right_x > self.speed) {
		p_dir_r = 1;
		p_dir_l = 0;
	}
	else if(v_right_x < -self.speed) {
		p_dir_r = 0;
		p_dir_l = 1;
	} else {
		p_dir_r = 0;
		p_dir_l = 0;
	}
	
	// forwards up and down
	if(v_forward_x > 0) {
		// right
		if(v_right_x > 0 && (v_up_x > self.speed/2 || v_up_y > self.speed/2)) {
			p_dir_u = 1;
			p_dir_d = 0;
		} else if (v_right_x > 0 && (v_up_x < -self.speed/2 || v_up_y < -self.speed/2)) {
			p_dir_u = 0;
			p_dir_d = 1;
		}
		// left 
		else if(v_right_x < 0 && (v_up_x > self.speed/2 || v_up_y < -self.speed/2)) {
			p_dir_u = 1;
			p_dir_d = 0;
		} else if (v_right_x < 0 && (v_up_x < -self.speed/2 || v_up_y > self.speed/2)) {
			p_dir_u = 0;
			p_dir_d = 1;
		} else {
			p_dir_u = 0;
			p_dir_d = 0;
		}
	}
	
	// backwards up and down
	if(v_forward_x < 0) {
		// right
		if(v_right_x > 0 && (v_up_x < -self.speed/2 || v_up_y > self.speed/2)) {
			p_dir_u = 1;
			p_dir_d = 0;
		} else if (v_right_x > 0 && (v_up_x > self.speed/2 || v_up_y < -self.speed/2)) {
			p_dir_u = 0;
			p_dir_d = 1;
		} 
		// left
		else if(v_right_x < 0 && (v_up_x < -self.speed/2 || v_up_y < -self.speed/2)) {
			p_dir_u = 1;
			p_dir_d = 0;
		} else if (v_right_x < 0 && (v_up_x > self.speed/2 || v_up_y > self.speed/2)) {
			p_dir_u = 0;
			p_dir_d = 1;
		} else {
			p_dir_u = 0;
			p_dir_d = 0;
		}
	}

	makevectors(self.mangle);
	
	/*
	// horizontal difference
	bprint(ftos(other.v_angle_y - self.mangle_x));
	if(other.v_angle_y - self.mangle_x <= self.t_width){
		bprint("direction approximately matched");
	}
	bprint("\n");
	
	// horizontal difference - FABS ind
	bprint(ftos(fabs(other.v_angle_y) - fabs(self.mangle_x)));
	if((fabs(other.v_angle_y) - fabs(self.mangle_x)) <= self.t_width){
		bprint("direction approximately matched");
	}
	bprint("\n");
	*/
	
	// horizontal difference - FABS wrap
	/*
	bprint(ftos(fabs(other.v_angle_y - self.mangle_x)));
	if((fabs(other.v_angle_y - self.mangle_x)) <= self.t_width){
		bprint("direction approximately matched");
	}
	bprint("\n");
	*/
	
	/*
	// horizontal difference - modangle
	bprint(ftos(anglemod(other.v_angle_y) - anglemod(self.mangle_x)));
	if((anglemod(other.v_angle_y) - anglemod(self.mangle_x)) <= self.t_width){
		bprint("direction approximately matched");
	}
	bprint("\n");	
	*/ 
	
	entity p = other;
        
        // Find out which direction player facing
        makevectors (p.v_angle);

        // Check if there is any things infront of the player
        traceline (p.origin , (p.origin+(v_forward * 500)) , FALSE , p);
};

void () trigger_lookdir_once =
{
	InitTrigger ();
	self.touch = lookdir_once_touch;
};

I left in the horizontal difference tests I created for reference. Lets talk about the gist of this code. The trigger_lookdir_once function is very similar to most triggers in the codebase so let’s skip straight to lookdir_once_touch as it has a lot of meat to unpack.

	float p_dir_r;
	float p_dir_l;
	float p_dir_f;
	float p_dir_b;
	float p_dir_u;
	float p_dir_d;
	
	makevectors(other.angles);
	vector o_forward = v_forward;
	vector o_up = v_up;
	vector o_right = v_right;

The variables being useds are the p_dir_r for direction right, direction left, direction forward, direction backward, direction up, and direction down for the player.
p = player
dir = direction
I then look at the other, which I believe is the mangle of the entity and run the makevectors function which creates v_forward, v_up, v_right. These I set immediately to o_forward, o_up, and o_right as the v_ vector variable is global in scope and I don’t want them to change. (I was being overly cautious so I ended up just going with the provided v_vector variables in the end) I use the

	// Forward and backward
	if(v_forward_x > self.speed) {
		p_dir_f = 1;
		p_dir_b = 0;
	}
	else if(v_forward_x < -self.speed) {
		p_dir_f = 0;
		p_dir_b = 1;
	} else {
		p_dir_f = 0;
		p_dir_b = 0;
	}

I then check to see if the player is moving forward, backward, left, right, up or down by checking the speed in that direction. If any of these things is true I then set them to 1, if not, then 0. This is not optimized in any way but simplified the debugging and testing of the function a great deal. Rinse and repeat for left and right.

	
	// forwards up and down
	if(v_forward_x > 0) {
		// right
		if(v_right_x > 0 && (v_up_x > self.speed/2 || v_up_y > self.speed/2)) {
			p_dir_u = 1;
			p_dir_d = 0;
		} else if (v_right_x > 0 && (v_up_x < -self.speed/2 || v_up_y < -self.speed/2)) {
			p_dir_u = 0;
			p_dir_d = 1;
		}
		// left 
		else if(v_right_x < 0 && (v_up_x > self.speed/2 || v_up_y < -self.speed/2)) {
			p_dir_u = 1;
			p_dir_d = 0;
		} else if (v_right_x < 0 && (v_up_x < -self.speed/2 || v_up_y > self.speed/2)) {
			p_dir_u = 0;
			p_dir_d = 1;
		} else {
			p_dir_u = 0;
			p_dir_d = 0;
		}
	}
	
	// backwards up and down
	if(v_forward_x < 0) {
		// right
		if(v_right_x > 0 && (v_up_x < -self.speed/2 || v_up_y > self.speed/2)) {
			p_dir_u = 1;
			p_dir_d = 0;
		} else if (v_right_x > 0 && (v_up_x > self.speed/2 || v_up_y < -self.speed/2)) {
			p_dir_u = 0;
			p_dir_d = 1;
		} 
		// left
		else if(v_right_x < 0 && (v_up_x < -self.speed/2 || v_up_y < -self.speed/2)) {
			p_dir_u = 1;
			p_dir_d = 0;
		} else if (v_right_x < 0 && (v_up_x > self.speed/2 || v_up_y > self.speed/2)) {
			p_dir_u = 0;
			p_dir_d = 1;
		} else {
			p_dir_u = 0;
			p_dir_d = 0;
		}
	}

Up and down were a totally different beast. You needed to find the up and down according to the forward and backward velocity and this still doesn’t account for absolute up or down. I can’t recall the exact rationality behind these checks but testing this trigger I found that you need to both test the mangle set by the entity and the speed of the player themselves interacting with the brush. I found that a very fast player can bypass the brush or even strife throught the brush without triggering it…

With this code you can determine the player’s directions and trigger events based on them.

So this works and I spent a good amount of time learning about how player movement works… BUT….

	entity p = other;
        
        // Find out which direction player facing
        makevectors (p.v_angle);

        // Check if there is any things infront of the player
        traceline (p.origin , (p.origin+(v_forward * 500)) , FALSE , p);

I get the player entity then I have a line that you would think might have been the totality of the code required for this function, but JUST DOESN’T result in meaningful data for what I need to do here, which is compare a player’s direction with that of a brush’s mangle.

Lastly I check to see if anything is infront of the player… OH SNAP! I have basically solved my 3rd option for solving this problem. This is the result:

Almost immediately after figuring this out… I created a relocate set of entities…

I then fell into the rabbit hole of creating a set of entities that allow you to turn your quake levels into indie horror games. Maybe more about that later.

Leave a comment