Page 6 of 6

Posted: Sun Apr 10, 2011 9:26 pm
by Spike
-1 and -2 mean 'up' or 'down', don't remember exactly which is which. So those are proper and required values.

worldmodel with angle -92 makes no sense, although its possible it was some hidden flag for some specific mod, but I'm unaware of which it could be.

Posted: Sun Apr 10, 2011 9:43 pm
by mh
Hmmm; this is where my mapping-fu hits it's limits, but I do believe you're correct there and that these are legal values indeed. Which means that this is a slightly more insidious problem as we have a case of legal values causing trouble.

I wonder if checking for - and clearing - an "angle" (not "angles") of -1 or -2 on map load would be a more robust solution? Probably not. Although these values are explicitly used for nothing else (up to and including map load time) aren't they, so maybe it is safe? Unfortunate that ID chose a field that has two different uses with no clear distinction. Would have been better to create a new _ field and throw it away in the compiler. (Edit - I think that's probably all rubbish as QC uses them, doesn't it?)

Angles set in worldspawn are nothing new by the way, e3m3 has them too.

Posted: Sun Apr 10, 2011 9:47 pm
by Spike
mh wrote:I wonder if checking for - and clearing - an "angle" (not "angles") of -1 or -2 on map load would be a more robust solution? Probably not. Although these values are explicitly used for nothing else (up to and including map load time) aren't they, so maybe it is safe?
Not possible. See SetMovedir in subs.qc.

Posted: Sun Apr 10, 2011 9:49 pm
by mh
You just trumped my edit! :lol:

Posted: Sun Apr 10, 2011 10:20 pm
by mh
Hmmm, SetMovedir just seems to be called from spawn functions, and it also clears the angles to (0, 0, 0) when done. At least in a clean QC it should.

Posted: Sun Apr 10, 2011 11:18 pm
by Spike
yeah, the problem is that its not called from every spawn function.
so if someone sets that setting and changes entity type or something, it has the wrong angles.
entity-type-specific mapper bug perhaps.
you still generally can't assume that its not a valid value unless you specifically check for entity names and mods, or something. you can make special-case hacks I suppose, but they may break some misc mods.

Posted: Sun Apr 10, 2011 11:21 pm
by mh
I've no interest in special-case hacks. Evil ugly little things.

I guess the most robust approach then is to revert back to what ID Quake did in cases where the angle being written is -1 or -2; something like what I've outlined here: http://forums.inside3d.com/viewtopic.php?t=3184&start=5

At least it feels less dirty than the previous hack I'd proposed...

Posted: Mon Apr 11, 2011 6:52 am
by metlslime
I've seen this before, and it's as you say, the result of better rounding with a yaw of 1. This happens in fitzquake even with protocol 15, since i do "better" rounding in all cases.

Technically it is a map bug because the mapper intended it to have a yaw of 0, but obviously engine changes that create symptoms can be considered engine bugs too.

Might be a good reason to revert to "bad" rounding for everything that isn't player aiming.

Posted: Mon Apr 11, 2011 4:54 pm
by mh
angles[1] is the only one that needs special handling though; [0] and [2] can keep the rounding.

Re: Avirox's Rotation Tutorial Adapted to NetQuake

Posted: Sat May 23, 2015 6:14 pm
by ericw
Was playing with this recently. gb's and baker's test maps are very cool.
I made another little test map that requires no special qc:
http://quaketastic.com/files/misc/bboxangletest.zip

The focus was to look at how engine bbox rotation could break old vanilla quake maps.
There's a drawbridge that's half open (func_train with "angles" "-32 0 0"). In the map/bsp, the bridge lies flat on the floor. Depending on whether then engine does bbox rotation you either clip through the half-open bridge, or walk up the ramp.
In hindsight I think it's basically a safe change (with regard to breaking vanilla content), because I think the bbox -not- rotation when you mess with "angles" could be considered a bug, and I think it's an unlikely kind of bug for mappers/modders to have exploited.


One note: my test map seems to give trouble in RMQe and DirectQ. As soon as you touch the bridge you sink in and get stuck. I know those aren't maintained, but this might be a useful debug test case if you're still using the same collision code in your research engine, mh,

Re: Avirox's Rotation Tutorial Adapted to NetQuake

Posted: Thu Feb 16, 2017 4:01 pm
by mh
What a fascinating re-read.
ericw wrote:One note: my test map seems to give trouble in RMQe and DirectQ. As soon as you touch the bridge you sink in and get stuck. I know those aren't maintained, but this might be a useful debug test case if you're still using the same collision code in your research engine, mh,
This was caused by the SV_RotateBBoxToAbsMinMax function I posted on the previous page. Reverting it to the code in Baker's original first post is sufficient to fix (at the expense of generating a larger bbox, of course).

I think this is an interesting enough problem to warrant further investigation. I don't curently have the capability to show server-side bboxes in my test engine, so I might port the rotation code to a Fitz-derivative and try to break it again with my messed-up function.

At the rate this thread is updating that might well be some time in 2019. :biggrin:

Re: Avirox's Rotation Tutorial Adapted to NetQuake

Posted: Mon Jul 09, 2018 10:17 pm
by Max_Salivan
Just got a real Half Life doors without setpointdoor for rotating.

Code: Select all

/*
Rotating doors are based on "Chris '06 Hinge Doors".
Doors needs to be made like in HalfLife using origin brush.
Swings from both sides, avelocity overshot issue resolved using .ltime.

RSTATE variable:
0 = idle/closed
1 = active

Due to some origin issues to be able to have the door swing in the correct
fashion depending on where the player hits it you will have to use the
setpointdoor float as a key in the door entity in the map editor.

Their names correspond to when looking down from on top of the door (bird's eye view):
Their angle, if the door is the long way from left to right it is HORIZONTAL. If it is
long from top to bottom it is VERTICAL. The next bit corresponds to which side of the door
has the origin brush which decides from where it will swing from. If the origin brush is
on the left then it is LEFT_HINGE. And if it is on the right it is RIGHT_HINGE. In terms
of VERTICAL doors the LEFT_HINGE would be on the lowest end of the door if looking at it
from the top, and the RIGHT_HINGE would be the top of the door if looking at it from the top
view.

TODO:make it more like CS,change setpointdoor
*/

/*
-=-=-=-
Defines
-=-=-=-
*/

float HORIZONTAL_LEFT_HINGE = 0;
float HORIZONTAL_RIGHT_HINGE = 1;
float VERTICAL_LEFT_HINGE = 2;
float VERTICAL_RIGHT_HINGE = 3;

.float RSTATE, reverse;
void() door_open;

// ------------------------------------------------
float(entity targ) infrontofdoor =
// ------------------------------------------------
{
	float sign = 1;
	float loX = self.mins_x + self.origin_x;
	float loY = self.mins_y + self.origin_y;

	float hiX = self.maxs_x + self.origin_x;
	float hiY = self.maxs_y + self.origin_y;

	float momentArmX = targ.origin_x - self.origin_x;
	float momentArmY = targ.origin_y - self.origin_y;
	if (loX > targ.origin_x)
	{
		if (targ.origin_y < loY)
		{
			if (fabs(momentArmY) > fabs(momentArmX))
				sign = (momentArmY < 0) ? 1 : -1;
			else
				sign = (momentArmX > 0) ? 1 : -1;
		}
		else if (targ.origin_y > hiY)
		{
			if (fabs(momentArmY) > fabs(momentArmX))
				sign = (momentArmY < 0) ? 1 : -1;
			else
				sign = (momentArmX < 0) ? 1 : -1;
		}
		else
			sign = (momentArmY < 0) ? 1 : -1;
	}
	else
	{
		if (targ.origin_x <= hiX)
		{
			if (targ.origin_y < loY)
				sign = (momentArmX > 0) ? 1 : -1;
			else if (targ.origin_y > hiY)
				sign = (momentArmX < 0) ? 1 : -1;
		}
		else if (targ.origin_y < loY)
		{
			if (fabs(momentArmY) > fabs(momentArmX))
				sign = (momentArmY > 0) ? 1 : -1;
			else
				sign = (momentArmX > 0) ? 1 : -1;
		}
		else if (targ.origin_y > hiY)
		{
			if (fabs(momentArmY) > fabs(momentArmX))
				sign = (momentArmY > 0) ? 1 : -1;
			else
				sign = (momentArmX < 0) ? 1 : -1;
		}
		else
			sign = (momentArmY > 0) ? 1 : -1;
	}
	if(sign == 1)
		return TRUE;
	else
		return FALSE;
};

// ------------------------------------------------
void() func_door_rotating =
// ------------------------------------------------
{
   if (self.sounds == 0)//no sound fx
   {
      precache_sound ("misc/null.wav");
      self.noise1 = "misc/null.wav";
      self.noise2 = "misc/null.wav";
      self.noise3 = "misc/null.wav";
   }
   if (self.sounds == 1)//generic
   {
      precache_sound ("door/open_generic.wav");
      precache_sound ("door/close_generic.wav");
      precache_sound ("door/squeek_generic.wav");
      self.noise1 = "door/open_generic.wav";
      self.noise2 = "door/close_generic.wav";
      self.noise3 = "door/squeek_generic.wav";
   }
   if (self.sounds == 2)//metal light door
   {
      precache_sound ("door/open_lmetal.wav");
      precache_sound ("door/close_lmetal.wav");
      precache_sound ("door/squeek_generic.wav");
      self.noise1 = "door/open_lmetal.wav";
      self.noise2 = "door/close_lmetal.wav";
      self.noise3 = "door/squeek_generic.wav";
   }
   if (self.sounds == 3)//metal heavy door
   {
      precache_sound ("door/open_metal.wav");
      precache_sound ("door/close_metal.wav");
      precache_sound ("door/squeek_metal.wav");
      self.noise1 = "door/open_metal.wav";
      self.noise2 = "door/close_metal.wav";
      self.noise3 = "door/squeek_metal.wav";
   }
   if (self.sounds == 4)//wood door
   {
      precache_sound ("door/open_wood.wav");
      precache_sound ("door/close_wood.wav");
      precache_sound ("door/squeek_wood.wav");
      self.noise1 = "door/open_wood.wav";
      self.noise2 = "door/close_wood.wav";
      self.noise3 = "door/squeek_wood.wav";

   }
       
	  self.solid = SOLID_BSP;
	  self.movetype = MOVETYPE_PUSH;
	  setorigin (self, self.origin);
	  setmodel (self, self.model);
	  self.classname = "func_door_rotating";
	  setsize (self, self.mins, self.maxs);
	  self.RSTATE = 0;

   if (!self.targetname)
      self.touch = door_open;
   
   self.nextthink = self.ltime + 9999999999999999999;
   self.think = door_open;
   
   if (!self.wait)
      self.wait = 0.1;
   
   if (self.targetname)
      self.use = door_open;
   
   self.avelocity = '0 0 0';

};

// ------------------------------------------------
void() door_closed =
// ------------------------------------------------
{
   self.avelocity = '0 0 0';
   self.RSTATE = 0;
   self.nextthink = self.ltime + 9999999999999999999;
   sound (self, CHAN_VOICE, self.noise2, 1, ATTN_IDLE);
   self.touch = door_open;

};

// ------------------------------------------------
void() door_closing =
// ------------------------------------------------
{
   self.nextthink = self.ltime + 1;
   self.think = door_closed;

   if (!self.reverse)
      self.avelocity = '0 -90 0';
   else
      self.avelocity = '0 90 0';

   sound (self, CHAN_VOICE, self.noise3, 1, ATTN_IDLE); 
};

// ------------------------------------------------
void() door_opened =
// ------------------------------------------------
{

   if (!self.targetname)
   {
      self.avelocity = '0 0 0';
      self.nextthink = self.ltime + 2;
      self.think = door_closing;
   }
};

// ------------------------------------------------
void() door_opening =
// ------------------------------------------------
{
   self.nextthink = self.ltime + 1;
   self.think = door_opened;

   if (!self.reverse)
      self.avelocity = '0 90 0';
   else
      self.avelocity = '0 -90 0';
};

// ------------------------------------------------
void() door_open =
// ------------------------------------------------
{

    self.avelocity = '0 0 0';
      self.touch = SUB_Null;

	if (!infrontofdoor(other))
		self.reverse = 1;

	if (infrontofdoor(other))
		self.reverse = 0;

   if (self.RSTATE == 0)
   {
      self.RSTATE = 1;
      door_opening ();
      sound (self, CHAN_BODY, self.noise1, 1, ATTN_IDLE);
   }

};