Tutorial: Rotating Brush Models for QuakeWorld
Moderator: InsideQC Admins
Tutorial: Rotating Brush Models for QuakeWorld
Have you ever wondered how to get entities like this:
.. to become like THIS:
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:
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):
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):
Next in world.c we need SV_ClipMoveToEntity. Find it and change it to look like the following:
Lets add some ints to the physent_t struct. Add these two if they are not already present in the struct:
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:
Finally we must fix up the player trace. Find PM_PlayerTrace and edit it accordingly:
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!
.. to become like THIS:
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!
- avirox
- Posts: 137
- Joined: Wed Aug 16, 2006 3:25 pm
Re: Tutorial: Rotating Brush Models for QuakeWorld
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.
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.
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
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)
-

frag.machine - Posts: 2090
- Joined: Sat Nov 25, 2006 1:49 pm
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...
-

goldenboy - Posts: 924
- Joined: Fri Sep 05, 2008 11:04 pm
- Location: Kiel
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
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.
-

goldenboy - Posts: 924
- Joined: Fri Sep 05, 2008 11:04 pm
- Location: Kiel
- 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.
-

goldenboy - Posts: 924
- Joined: Fri Sep 05, 2008 11:04 pm
- Location: Kiel
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).
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).
- Spike
- Posts: 2892
- Joined: Fri Nov 05, 2004 3:12 am
- Location: UK
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....
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;
};
- r00k
- Posts: 1110
- Joined: Sat Nov 13, 2004 10:39 pm
Return to Programming Tutorials
Who is online
Users browsing this forum: No registered users and 1 guest