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…
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:
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.