Tutorial: Rotating Brush Models for QuakeWorld

Post tutorials on how to do certain tasks within game or engine code here.
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Tutorial: Rotating Brush Models for QuakeWorld

Post by avirox »

Have you ever wondered how to get entities like this:
Image

.. to become like THIS:
Image

No? Well neither have I until now!

Here is a little tutorial on implementing rotating brush support to QuakeWorld. What are rotating brush models? Well, they're basically things like spinning fans, rotating trains and rotating doors. Basically the stuff you see in quake 2/half-life/every game made after those two. Interestingly, most of this functionality is actually already present within winquake. However, quakeworld lacks the quake 2 references that WQ has, and also QW player traces needs to be accounted for.

First I'd like to credit the sources for this code. Namely: id software, MrG, LordHavoc, Spike, and the little leprechaun that sits on my shoulder and tells me to burn things.

Begin the beguine
Open up sv_phys.c and prepare to make it your bitch. Find SV_Physics_Pusher and add the following function right above it:

Code: Select all

/*
============
SV_PushRotate

============
*/
void SV_PushRotate (edict_t *pusher, float movetime)
{
	int			i, e;
	edict_t		*check, *block;
	vec3_t		move, a, amove;
	vec3_t		entorig, pushorig;
	int			num_moved;
	edict_t		*moved_edict[MAX_EDICTS];
	vec3_t		moved_from[MAX_EDICTS];
	vec3_t		org, org2;
	vec3_t		forward, right, up;

	if (!pusher->v.avelocity[0] && !pusher->v.avelocity[1] && !pusher->v.avelocity[2])
	{
		pusher->v.ltime += movetime;
		return;
	}

	for (i=0 ; i<3 ; i++)
		amove[i] = pusher->v.avelocity[i] * movetime;

	VectorSubtract (vec3_origin, amove, a);
	AngleVectors (a, forward, right, up);

	VectorCopy (pusher->v.angles, pushorig);
	
// move the pusher to it's final position

	VectorAdd (pusher->v.angles, amove, pusher->v.angles);
	pusher->v.ltime += movetime;
	SV_LinkEdict (pusher, false);


// see if any solid entities are inside the final position
	num_moved = 0;
	check = NEXT_EDICT(sv.edicts);
	for (e=1 ; e<sv.num_edicts ; e++, check = NEXT_EDICT(check))
	{
		if (check->free)
			continue;
		if (check->v.movetype == MOVETYPE_PUSH
		|| check->v.movetype == MOVETYPE_NONE
		|| check->v.movetype == MOVETYPE_FOLLOW
		|| check->v.movetype == MOVETYPE_NOCLIP)
			continue;

	// if the entity is standing on the pusher, it will definately be moved
		if ( ! ( ((int)check->v.flags & FL_ONGROUND)
		&& PROG_TO_EDICT(check->v.groundentity) == pusher) )
		{
			if ( check->v.absmin[0] >= pusher->v.absmax[0]
			|| check->v.absmin[1] >= pusher->v.absmax[1]
			|| check->v.absmin[2] >= pusher->v.absmax[2]
			|| check->v.absmax[0] <= pusher->v.absmin[0]
			|| check->v.absmax[1] <= pusher->v.absmin[1]
			|| check->v.absmax[2] <= pusher->v.absmin[2] )
				continue;

		// see if the ent's bbox is inside the pusher's final position
			if (!SV_TestEntityPosition (check))
				continue;
		}

	// remove the onground flag for non-players
		if (check->v.movetype != MOVETYPE_WALK)
			check->v.flags = (int)check->v.flags & ~FL_ONGROUND;
		
		VectorCopy (check->v.origin, entorig);
		VectorCopy (check->v.origin, moved_from[num_moved]);
		moved_edict[num_moved] = check;
		num_moved++;

		// calculate destination position
		VectorSubtract (check->v.origin, pusher->v.origin, org);
		org2[0] = DotProduct (org, forward);
		org2[1] = -DotProduct (org, right);
		org2[2] = DotProduct (org, up);
		VectorSubtract (org2, org, move);

		// try moving the contacted entity 
		pusher->v.solid = SOLID_NOT;
		SV_PushEntity (check, move);
		pusher->v.solid = SOLID_BSP;

	// if it is still inside the pusher, block
		block = SV_TestEntityPosition (check);
		if (block)
		{	// fail the move
			if (check->v.mins[0] == check->v.maxs[0])
				continue;
			if (check->v.solid == SOLID_NOT || check->v.solid == SOLID_TRIGGER)
			{	// corpse
				check->v.mins[0] = check->v.mins[1] = 0;
				VectorCopy (check->v.mins, check->v.maxs);
				continue;
			}
			
			VectorCopy (entorig, check->v.origin);
			SV_LinkEdict (check, true);

			VectorCopy (pushorig, pusher->v.angles);
			SV_LinkEdict (pusher, false);
			pusher->v.ltime -= movetime;

			// if the pusher has a "blocked" function, call it
			// otherwise, just stay in place until the obstacle is gone
			if (pusher->v.blocked)
			{
				pr_global_struct->self = EDICT_TO_PROG(pusher);
				pr_global_struct->other = EDICT_TO_PROG(check);
				PR_ExecuteProgram (pusher->v.blocked);
			}
			
		// move back any entities we already moved
			for (i=0 ; i<num_moved ; i++)
			{
				VectorCopy (moved_from[i], moved_edict[i]->v.origin);
				VectorSubtract (moved_edict[i]->v.angles, amove, moved_edict[i]->v.angles);
				SV_LinkEdict (moved_edict[i], false);
			}
			return;
		}
		else
		{
			VectorAdd (check->v.angles, amove, check->v.angles);
		}
	}

	
}

Now we must edit SV_Physics_Pusher. Compare your SV_Physics_Pusher function with the one below, and add the code in //ROTATE START and //ROTATE END to yours (PS. in some engines it may be possible to just replace your whole function altogether with the one below):

Code: Select all

void SV_Physics_Pusher (edict_t *ent)
{
	float	thinktime;
	float	oldltime;
	float	movetime;

	oldltime = ent->v.ltime;
	
	thinktime = ent->v.nextthink;
	if (thinktime < ent->v.ltime + sv_frametime)
	{
		movetime = thinktime - ent->v.ltime;
		if (movetime < 0)
			movetime = 0;
	}
	else
		movetime = sv_frametime;

	if (movetime)
	{
//ROTATE START
		if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2])
			SV_PushRotate (ent, sv_frametime);
		else
//ROTATE END
			SV_PushMove (ent, movetime);	// advances ent->v.ltime if not blocked
	}
		
	if (thinktime > oldltime && thinktime <= ent->v.ltime)
	{
		ent->v.nextthink = 0;
		pr_global_struct->time = sv.time;
		pr_global_struct->self = EDICT_TO_PROG(ent);
		pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
		PR_ExecuteProgram (ent->v.think);
		if (ent->free)
			return;
	}
}
We're done with that file. Now on to world.c

Go to SV_LinkEdict and edit it thusly (again, look at //ROTATE START and //ROTATE END references):

Code: Select all

void SV_LinkEdict (edict_t *ent, qbool touch_triggers)
{
	areanode_t	*node;
	
	if (ent->area.prev)
		SV_UnlinkEdict (ent);	// unlink from old position
		
	if (ent == sv.edicts)
		return;		// don't add the world

	if (ent->free)
		return;

// set the abs box
// ROTATE START
	if (ent->v.solid == SOLID_BSP && 
	(ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
	{	// expand for rotation
		float		max, v;
		int			i;

max = DotProduct(ent->v.mins, ent->v.mins);

v = DotProduct(ent->v.maxs, ent->v.maxs);

if (max < v)

	max = v;

	max = sqrt(max);

	for (i=0 ; i<3 ; i++)

		{

		ent->v.absmin[i] = ent->v.origin[i] - max;

		ent->v.absmax[i] = ent->v.origin[i] + max;

		}

	}
	else
// ROTATE END
	{
		VectorAdd (ent->v.origin, ent->v.mins, ent->v.absmin);
		VectorAdd (ent->v.origin, ent->v.maxs, ent->v.absmax);
	}

	//
// to make items easier to pick up and allow them to be grabbed off
// of shelves, the abs sizes are expanded
	//
	if ((int)ent->v.flags & FL_ITEM)
	{
		ent->v.absmin[0] -= 15;
		ent->v.absmin[1] -= 15;
		ent->v.absmax[0] += 15;
		ent->v.absmax[1] += 15;
	}
	else
	{	// because movement is clipped an epsilon away from an actual edge,
		// we must fully check even when bounding boxes don't quite touch
		ent->v.absmin[0] -= 1;
		ent->v.absmin[1] -= 1;
		ent->v.absmin[2] -= 1;
		ent->v.absmax[0] += 1;
		ent->v.absmax[1] += 1;
		ent->v.absmax[2] += 1;
	}
	
// link to PVS leafs
	if (ent->v.modelindex)
		SV_LinkToLeafs (ent);
	else
		ent->num_leafs = 0;

	if (ent->v.solid == SOLID_NOT)
		return;

// find the first node that the ent's box crosses
	node = sv_areanodes;
	while (1)
	{
		if (node->axis == -1)
			break;
		if (ent->v.absmin[node->axis] > node->dist)
			node = node->children[0];
		else if (ent->v.absmax[node->axis] < node->dist)
			node = node->children[1];
		else
			break;		// crosses the node
	}
	
// link it in	

	if (ent->v.solid == SOLID_TRIGGER)
		InsertLinkBefore (&ent->area, &node->trigger_edicts);
	else
		InsertLinkBefore (&ent->area, &node->solid_edicts);
	
// if touch_triggers, touch all entities at this node and decend for more
	if (touch_triggers)
		SV_TouchLinks ( ent, sv_areanodes );
}
Next in world.c we need SV_ClipMoveToEntity. Find it and change it to look like the following:

Code: Select all

trace_t SV_ClipMoveToEntity (edict_t *ent, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end)
{
		trace_t		trace;
	vec3_t		offset;
	vec3_t		start_l, end_l;
	hull_t		*hull;

// fill in a default trace
	memset (&trace, 0, sizeof(trace_t));
	trace.fraction = 1;
	trace.allsolid = true;
	VectorCopy (end, trace.endpos);

// get the clipping hull
	hull = SV_HullForEntity (ent, mins, maxs, offset);

	VectorSubtract (start, offset, start_l);
	VectorSubtract (end, offset, end_l);

// ROTATE START
	// rotate start and end into the models frame of reference
	if (ent->v.solid == SOLID_BSP && 
	(ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
	{
		vec3_t	a;
		vec3_t	forward, right, up;
		vec3_t	temp;

		AngleVectors (ent->v.angles, forward, right, up);

		VectorCopy (start_l, temp);
		start_l[0] = DotProduct (temp, forward);
		start_l[1] = -DotProduct (temp, right);
		start_l[2] = DotProduct (temp, up);

		VectorCopy (end_l, temp);
		end_l[0] = DotProduct (temp, forward);
		end_l[1] = -DotProduct (temp, right);
		end_l[2] = DotProduct (temp, up);
	}
// ROTATE END

// trace a line through the apropriate clipping hull
	//SV_RecursiveHullCheck (hull, hull->firstclipnode, 0, 1, start_l, end_l, &trace);
	if (ent->v.solid != SOLID_BSP)
	{
		ent->v.angles[0]*=-1;	//carmack made bsp models rotate wrongly.
		trace = CM_HullTrace (hull, start_l, end_l);
		ent->v.angles[0]*=-1;
	}
	else
		trace = CM_HullTrace (hull, start_l, end_l);

// ROTATE START
	// rotate endpos back to world frame of reference
	if (ent->v.solid == SOLID_BSP && 
	(ent->v.angles[0] || ent->v.angles[1] || ent->v.angles[2]) )
	{
		vec3_t	a;
		vec3_t	forward, right, up;
		vec3_t	temp;

		if (trace.fraction != 1)
		{
			VectorSubtract (vec3_origin, ent->v.angles, a);
			AngleVectors (a, forward, right, up);

			VectorCopy (trace.endpos, temp);
			trace.endpos[0] = DotProduct (temp, forward);
			trace.endpos[1] = -DotProduct (temp, right);
			trace.endpos[2] = DotProduct (temp, up);

			VectorCopy (trace.plane.normal, temp);
			trace.plane.normal[0] = DotProduct (temp, forward);
			trace.plane.normal[1] = -DotProduct (temp, right);
			trace.plane.normal[2] = DotProduct (temp, up);
		}
	}
// ROTATE END

// fix trace up by the offset
	VectorAdd (trace.endpos, offset, trace.endpos);

// did we clip the move?
	if (trace.fraction < 1 || trace.startsolid )
		trace.e.ent = ent;

	return trace;
}
Lets add some ints to the physent_t struct. Add these two if they are not already present in the struct:

Code: Select all

	vec3_t		angles;
	int			solid;	// xavior hax
Now we need to work on player collision. Go to the function AddLinksToPmove in sv_user.c and add the following lines in the if (check->v.solid == SOLID_BSP) { } segment:

Code: Select all

				VectorCopy (check->v.angles, pe->angles);		// for rot brushes
				pe->solid = SOLID_BSP;
Finally we must fix up the player trace. Find PM_PlayerTrace and edit it accordingly:

Code: Select all

trace_t PM_PlayerTrace (vec3_t start, vec3_t end)
{
	trace_t		trace, total;
	vec3_t		offset;
	vec3_t		start_l, end_l;
	hull_t		*hull;
	int			i;
	physent_t	*pe;
	vec3_t		mins, maxs, tracemins, tracemaxs;

// fill in a default trace
	memset (&total, 0, sizeof(trace_t));
	total.fraction = 1;
	total.e.entnum = -1;
	VectorCopy (end, total.endpos);

	PM_TraceBounds(start, end, tracemins, tracemaxs);

	for (i=0 ; i< pmove.numphysent ; i++)
	{
		pe = &pmove.physents[i];

	// get the clipping hull
		if (pe->model)
		{
			if (sv_client->is_crouching == 1)
				hull = &pmove.physents[i].model->hulls[3];
			else
				hull = &pmove.physents[i].model->hulls[1];

			//if (i > 0 && PM_CullTraceBox(tracemins, tracemaxs, pe->origin, pe->model->mins, pe->model->maxs, hull->clip_mins, hull->clip_maxs))
			//	continue;

			VectorSubtract (hull->clip_mins, player_mins, offset);
			VectorAdd (offset, pe->origin, offset);
		}
		else
		{
			VectorSubtract (pe->mins, player_maxs, mins);
			VectorSubtract (pe->maxs, player_mins, maxs);

			if (PM_CullTraceBox(tracemins, tracemaxs, pe->origin, mins, maxs, vec3_origin, vec3_origin))
				continue;

			hull = CM_HullForBox (mins, maxs);
			VectorCopy (pe->origin, offset);
		}

		VectorSubtract (start, offset, start_l);
		VectorSubtract (end, offset, end_l);

// ROTATE START
	// rotate start and end into the models frame of reference
		if (pe->solid == SOLID_BSP &&
	(pe->angles[0] || pe->angles[1] || pe->angles[2]) )
	{
		vec3_t	a;
		vec3_t	forward, right, up;
		vec3_t	temp;

		AngleVectors (pe->angles, forward, right, up);

		VectorCopy (start_l, temp);
		start_l[0] = DotProduct (temp, forward);
		start_l[1] = -DotProduct (temp, right);
		start_l[2] = DotProduct (temp, up);

		VectorCopy (end_l, temp);
		end_l[0] = DotProduct (temp, forward);
		end_l[1] = -DotProduct (temp, right);
		end_l[2] = DotProduct (temp, up);
	}
// ROTATE END

		// trace a line through the apropriate clipping hull
		trace = CM_HullTrace (hull, start_l, end_l);
// ROTATE START
	// rotate endpos back to world frame of reference
	if (pe->solid == SOLID_BSP &&
	(pe->angles[0] || pe->angles[1] || pe->angles[2]) )
	{
		vec3_t	a;
		vec3_t	forward, right, up;
		vec3_t	temp;

		if (trace.fraction != 1)
		{
			VectorSubtract (vec3_origin, pe->angles, a);
			AngleVectors (a, forward, right, up);

			VectorCopy (trace.endpos, temp);
			trace.endpos[0] = DotProduct (temp, forward);
			trace.endpos[1] = -DotProduct (temp, right);
			trace.endpos[2] = DotProduct (temp, up);

			VectorCopy (trace.plane.normal, temp);
			trace.plane.normal[0] = DotProduct (temp, forward);
			trace.plane.normal[1] = -DotProduct (temp, right);
			trace.plane.normal[2] = DotProduct (temp, up);
		}
	}
// ROTATE END

		// fix trace up by the offset
		VectorAdd (trace.endpos, offset, trace.endpos);

		if (trace.allsolid)
			trace.startsolid = true;
		if (trace.startsolid)
			trace.fraction = 0;

		// did we clip the move?
		if (trace.fraction < total.fraction)
		{
			total = trace;
			total.e.entnum = i;
		}

	}

	return total;
}
And that's it! You should now have functional rotating brush support in your quakeworld server. If you want to make a test in your QC mod, follow the one at the bottom of this page. Feel free to leave comments/suggestions/death threats. If there is something that's not compiling, also let me know!

Enjoy, and happy new year!
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Tutorial: Rotating Brush Models for QuakeWorld

Post by Baker »

Thanks!

Btw, your "modified FuhQuake" has vwep and alpha textures support right?

Future QW tutorial wish list: Animation interpolation tutorial for Quakeworld"(ezQuake has this feature, correct?)

After reading tutorials like this, part of me wants to focus on ripping and borrowing from your modified FuhQuake and ezQuake and up-strength the ZQuake engine's capabilities into a "clean total conversion oriented" all purpose Quake engine.
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Post by avirox »

FuhQuake has interpolation similar to ezquake actually, but it's only applied to specified models (hard-coded, in fact. Why this is, I'm still not sure. I suppose one day I'll modify it to support everything and see how it looks..
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Excellent tut, avirox.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
goldenboy
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel
Contact:

Post by goldenboy »

And that's it! You should now have functional rotating brush support in your quakeworld server. If you want to make a test in your QC mod, follow the one at the bottom of this page. Feel free to leave comments/suggestions/death threats. If there is something that's not compiling, also let me know!
Which engines support this? I assume FTE should support self.avelocity_(x,y,z) based rotation right?

I put this

Code: Select all

void() func_rotating =

{

	self.solid = SOLID_BSP;

	self.movetype = MOVETYPE_PUSH;

	setorigin (self, self.origin);

	setmodel (self, self.model);

	self.classname = "func_rotating";

	setsize (self, self.mins, self.maxs);



	if (!self.speed)

		self.speed = 100;



	if (self.spawnflags & 2) // reverse direction

		self.speed = 0 - self.speed;



	if (self.spawnflags & 64) // not solid

		self.solid = SOLID_NOT;



	if (self.spawnflags & 4)

	{

		self.avelocity_z = self.speed;

		self.avelocity_x = 0;

		self.avelocity_y = 0;

	}

	else if (self.spawnflags & 8)

	{

		self.avelocity_z = 0;

		self.avelocity_x = self.speed;

		self.avelocity_y = 0;

	}

	else

	{

		self.avelocity_z = 0;

		self.avelocity_x = 0;

		self.avelocity_y = self.speed;

	}

};
into RMQ, and then I made a testmap with a func_rotating.

I then set the entity's "origin" to the bmodel's origin.

Tried the map in darkplaces and FTE - the bmodel was nowhere to be found, not even when I noclipped around.

Next, I moved the whole map so the func_rotating was centered at 0 0 0. I tried both setting "origin" to that, and settig no origin key.

The brush appeared in the map now, but wasn't rotating.

No spawnflags set.

This whole shit is SO frustrating. I've tried this a number of times now, first with the rotating door code from Quake Life, now with the simple func_rotating code from the tutorial, and NOTHING WORKS.

Edit: Closer investigation reveals that FTE doesn't have this code. Hmm, OK.

A case for a new standard...
avirox
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Post by avirox »

FTE has rotating brush ents, actually, but it looks different in code :) Also, you need to compile your map's rotating entity with an origin brush.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

velocity and thus avelocity only work on MOVETYPE_PUSH entities if they have a valid nextthink.
goldenboy
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel
Contact:

Post by goldenboy »

Spike wrote:velocity and thus avelocity only work on MOVETYPE_PUSH entities if they have a valid nextthink.
OK. That seems to do something :)

Code: Select all

self.nextthink = time;
and it's rotating, yay - although DP dumps me back in the console with "QC function self.think is missing", which is kinda logical -

It works when I just point self.think at func_rotating. There must be a better way though...

And I just noticed that what I have now rotates in DP, but not in FTE :-/ aaargh.

xavior, there are no Q1 map compilers that support origin brushes, so I just put "origin" "x y z" manually in the map editor. That seems a little weird, too, because what I get in the engine is something else (64 units lower) than what I get in the editor...

Anyway, the qc from the quakesrc.org tutorial does not work - it works in DP when I add

Code: Select all

self.think = func_rotating;
self.nextthink = time;
but not in FTE.

Example code for a func_rotating that works in DP and FTE would be much appreciated.
Last edited by goldenboy on Sun Jan 03, 2010 3:05 am, edited 1 time in total.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Post by Baker »

2 questions:

1. Can someone upload a map sample that works with this or am I going to have to use a Half-Life map?

2. Does ANY existing map compiler for Q1 support origin brushes? Just wondering.
goldenboy
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel
Contact:

Post by goldenboy »

1. Yes, shortly, but only works in DP with the above QC code;

2. No.
goldenboy
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel
Contact:

Post by goldenboy »

Code: Select all

/*QUAKED func_rotating (0 .5 .8) ? x REVERSE Z_AXIS X_AXIS x x NONSOLID 
Origin-rotating bmodel - experimental

"speed"         rotation speed (100 default)
"origin"        origin vector, x y z
*/

void() func_rotating =
{
        self.solid = SOLID_BSP;
        self.movetype = MOVETYPE_PUSH;
        self.think = func_rotating;
        setorigin (self, self.origin);
        setmodel (self, self.model);
        self.classname = "func_rotating";
        setsize (self, self.mins, self.maxs);

        if (!self.speed)
                self.speed = 100;

        if (self.spawnflags & 2) // reverse direction
                self.speed = 0 - self.speed;

        if (self.spawnflags & 64) // not solid
                self.solid = SOLID_NOT;

        if (self.spawnflags & 4)
        {
                self.avelocity_z = self.speed;
                self.avelocity_x = 0;
                self.avelocity_y = 0;
        }
        else if (self.spawnflags & 8)
        {
                self.avelocity_z = 0;
                self.avelocity_x = self.speed;
                self.avelocity_y = 0;
        }
        else
        {
                self.avelocity_z = 0;
                self.avelocity_x = 0;
                self.avelocity_y = self.speed;
        }

        self.nextthink = time;  // next frame; 
};

http://www.quaketastic.com/upload/files ... origin.zip

The map contains a func_rotating in the form of a cube.

The qc and map combination works in DP (rotates).

"origin" was set manually.

http://www.4shared.com/file/187101836/6 ... rigin.html

Small vid of a happily spinning yellow block - in DP.
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Post by r00k »

Nice!
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

at spawn, time = 0
self.nextthink = time = 0; does nothing.

use:

self.think = SUB_Null;
self.nextthink = self.ltime + 9999999;

(the self.ltime term is optional, yeah, okay, it'll be 0, but hey, it highlights the difference between movetype_push and any other ent).
goldenboy
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel
Contact:

Post by goldenboy »

No luck. With the new qc, it still spins in DP, but not in FTE.

It's drawn in the correct place, just doesn't start to rotate :)

I tried a bit of stuff like self.nextthink = time + 0.1 and similar, but no luck.
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Post by r00k »

you dont need to .think back to the function. the physics code will rotate if the avelocity != 0. You DO need to set self.ltime = time + 0.1; maybe ?

Here's how Hipnotic is doing it....

Code: Select all


void () RotateTargets =
{
	local entity ent;
	local vector vx;
	local vector vy;
	local vector vz;
	local vector org;

	makevectors (self.angles);
	ent = find (world, targetname, self.target);
	while (ent)
	{
		if ((ent.rotate_type == OBJECT_SETORIGIN))
		{
			org = ent.oldorigin;
			vx = (v_forward * org_x);
			vy = (v_right * org_y);
			vy = (vy * CONTENT_EMPTY);
			vz = (v_up * org_z);
			ent.neworigin = ((vx + vy) + vz);
			setorigin (ent, (ent.neworigin + self.origin));
		}
		else
		{
			if ((ent.rotate_type == OBJECT_ROTATE))
			{
				ent.angles = self.angles;
				org = ent.oldorigin;
				vx = (v_forward * org_x);
				vy = (v_right * org_y);
				vy = (vy * CONTENT_EMPTY);
				vz = (v_up * org_z);
				ent.neworigin = ((vx + vy) + vz);
				setorigin (ent, (ent.neworigin + self.origin));
			}
			else
			{
				org = ent.oldorigin;
				vx = (v_forward * org_x);
				vy = (v_right * org_y);
				vy = (vy * CONTENT_EMPTY);
				vz = (v_up * org_z);
				ent.neworigin = ((vx + vy) + vz);
				ent.neworigin = ((self.origin - self.oldorigin) + (ent.neworigin - ent.oldorigin));
				ent.velocity = ((ent.neworigin - ent.origin) * 25);
			}
		}
		ent = find (ent, targetname, self.target);
	}
};

void () RotateTargetsFinal =
{
	local entity ent;

	ent = find (world, targetname, self.target);
	while (ent)
	{
		ent.velocity = VEC_ORIGIN;
		if ((ent.rotate_type == OBJECT_ROTATE))
		{
			ent.angles = self.angles;
		}
		ent = find (ent, targetname, self.target);
	}
};

void () SetTargetOrigin =
{
	local entity ent;

	ent = find (world, targetname, self.target);
	while (ent)
	{
		if ((ent.rotate_type == OBJECT_MOVEWALL))
		{
			setorigin (ent, ((self.origin - self.oldorigin) + (ent.neworigin - ent.oldorigin)));
		}
		else
		{
			setorigin (ent, (ent.neworigin + self.origin));
		}
		ent = find (ent, targetname, self.target);
	}
};

void () LinkRotateTargets =
{
	local entity ent;
	local vector tempvec;

	self.oldorigin = self.origin;
	ent = find (world, targetname, self.target);
	while (ent)
	{
		if ((ent.classname == "rotate_object"))
		{
			ent.rotate_type = OBJECT_ROTATE;
			ent.oldorigin = (ent.origin - self.oldorigin);
			ent.neworigin = (ent.origin - self.oldorigin);
			ent.owner = self;
		}
		else
		{
			if ((ent.classname == "func_movewall"))
			{
				ent.rotate_type = OBJECT_MOVEWALL;
				tempvec = ((ent.absmin + ent.absmax) * 0.5);
				ent.oldorigin = (tempvec - self.oldorigin);
				ent.neworigin = ent.oldorigin;
				ent.owner = self;
			}
			else
			{
				ent.rotate_type = OBJECT_SETORIGIN;
				ent.oldorigin = (ent.origin - self.oldorigin);
				ent.neworigin = (ent.origin - self.oldorigin);
			}
		}
		ent = find (ent, targetname, self.target);
	}
};

void () rotate_object =
{
	self.classname = "rotate_object";
	self.solid = SOLID_NOT;
	self.movetype = MOVETYPE_NONE;
	setmodel (self, self.model);
	setsize (self, self.mins, self.maxs);
	self.think = SUB_Null;
};

void () rotate_door_think2 =
{
	local float t;

	t = (time - self.ltime);
	self.ltime = time;
	self.frame = (TRUE - self.frame);
	self.angles = self.dest;
	if ((self.state == STATE_OPENING))
	{
		self.state = STATE_OPEN;
	}
	else
	{
		if ((self.spawnflags & STAYOPEN))
		{
			rotate_door_group_reversedirection ();
			return;
		}
		self.state = STATE_CLOSED;
	}
	sound (self, CHAN_VOICE, self.noise3, TRUE, ATTN_NORM);
	self.think = SUB_Null;
	RotateTargetsFinal ();
};

void () rotate_door_think =
{
	local float t;

	t = (time - self.ltime);
	self.ltime = time;
	if ((time < self.endtime))
	{
		self.angles = (self.angles + (self.rotate * t));
		RotateTargets ();
	}
	else
	{
		self.angles = self.dest;
		RotateTargets ();
		self.think = rotate_door_think2;
	}
	self.nextthink = (time + 0.01);
};

void () rotate_door_reversedirection =
{
	local vector start;

	self.frame = (TRUE - self.frame);
	if ((self.state == STATE_CLOSING))
	{
		start = self.dest1;
		self.dest = self.dest2;
		self.state = STATE_OPENING;
	}
	else
	{
		start = self.dest2;
		self.dest = self.dest1;
		self.state = STATE_CLOSING;
	}
	sound (self, CHAN_VOICE, self.noise2, TRUE, ATTN_NORM);
	self.rotate = ((self.dest - start) * (TRUE / self.speed));
	self.think = rotate_door_think;
	self.nextthink = (time + 0.02);
	self.endtime = ((time + self.speed) - (self.endtime - time));
	self.ltime = time;
};

void () rotate_door_group_reversedirection =
{
	local string name;

	if (self.group)
	{
		name = self.group;
		self = find (world, group, name);
		while (self)
		{
			rotate_door_reversedirection ();
			self = find (self, group, name);
		}
	}
	else
	{
		rotate_door_reversedirection ();
	}
};

void () rotate_door_use =
{
	local entity t;
	local vector start;

	if (((self.state != STATE_OPEN) && (self.state != STATE_CLOSED)))
	{
		return;
	}
	if (!self.cnt)
	{
		self.cnt = TRUE;
		LinkRotateTargets ();
	}
	self.frame = (TRUE - self.frame);
	if ((self.state == STATE_CLOSED))
	{
		start = self.dest1;
		self.dest = self.dest2;
		self.state = STATE_OPENING;
	}
	else
	{
		start = self.dest2;
		self.dest = self.dest1;
		self.state = STATE_CLOSING;
	}
	sound (self, CHAN_VOICE, self.noise2, TRUE, ATTN_NORM);
	self.rotate = ((self.dest - start) * (TRUE / self.speed));
	self.think = rotate_door_think;
	self.nextthink = (time + 0.01);
	self.endtime = (time + self.speed);
	self.ltime = time;
};

void () func_rotate_door =
{
	if (!self.target)
	{
		objerror ("rotate_door without target.");
	}
	self.dest1 = VEC_ORIGIN;
	self.dest2 = self.angles;
	self.angles = self.dest1;
	if (!self.speed)
	{
		self.speed = FL_SWIM;
	}
	self.cnt = FALSE;
	if (!self.dmg)
	{
		self.dmg = FL_SWIM;
	}
	else
	{
		if ((self.dmg < FALSE))
		{
			self.dmg = FALSE;
		}
	}
	if ((self.sounds == FALSE))
	{
		self.sounds = TRUE;
	}
	if ((self.sounds == TRUE))
	{
		precache_sound ("doors/latch2.wav");
		precache_sound ("doors/winch2.wav");
		precache_sound ("doors/drclos4.wav");
		self.noise1 = "doors/latch2.wav";
		self.noise2 = "doors/winch2.wav";
		self.noise3 = "doors/drclos4.wav";
	}
	if ((self.sounds == FL_SWIM))
	{
		precache_sound ("doors/airdoor1.wav");
		precache_sound ("doors/airdoor2.wav");
		self.noise2 = "doors/airdoor1.wav";
		self.noise1 = "doors/airdoor2.wav";
		self.noise3 = "doors/airdoor2.wav";
	}
	if ((self.sounds == MOVETYPE_WALK))
	{
		precache_sound ("doors/basesec1.wav");
		precache_sound ("doors/basesec2.wav");
		self.noise2 = "doors/basesec1.wav";
		self.noise1 = "doors/basesec2.wav";
		self.noise3 = "doors/basesec2.wav";
	}
	self.solid = SOLID_NOT;
	self.movetype = MOVETYPE_NONE;
	setmodel (self, self.model);
	setorigin (self, self.origin);
	setsize (self, self.mins, self.maxs);
	self.state = STATE_CLOSED;
	self.use = rotate_door_use;
	self.think = SUB_Null;
};
Post Reply