Forum

Tutorial: Rotating Brush Models for QuakeWorld

Post tutorials on how to do certain tasks within game or engine code here.

Moderator: InsideQC Admins

Tutorial: Rotating Brush Models for QuakeWorld

Postby avirox » Fri Jan 01, 2010 8:54 pm

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!
avirox
 
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Re: Tutorial: Rotating Brush Models for QuakeWorld

Postby Baker » Fri Jan 01, 2010 9:37 pm

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

Postby avirox » Sat Jan 02, 2010 10:47 pm

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..
avirox
 
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Postby frag.machine » Sat Jan 02, 2010 11:09 pm

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

Postby goldenboy » Sat Jan 02, 2010 11:50 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...
User avatar
goldenboy
 
Posts: 924
Joined: Fri Sep 05, 2008 11:04 pm
Location: Kiel

Postby avirox » Sun Jan 03, 2010 12:35 am

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.
avirox
 
Posts: 137
Joined: Wed Aug 16, 2006 3:25 pm

Postby Spike » Sun Jan 03, 2010 1:56 am

velocity and thus avelocity only work on MOVETYPE_PUSH entities if they have a valid nextthink.
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby goldenboy » Sun Jan 03, 2010 2:37 am

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

Postby Baker » Sun Jan 03, 2010 2:45 am

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

Postby goldenboy » Sun Jan 03, 2010 3:06 am

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

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

Postby goldenboy » Sun Jan 03, 2010 3:10 am

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

Postby r00k » Sun Jan 03, 2010 5:57 am

Nice!
r00k
 
Posts: 1110
Joined: Sat Nov 13, 2004 10:39 pm

Postby Spike » Sun Jan 03, 2010 6:41 am

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).
Spike
 
Posts: 2892
Joined: Fri Nov 05, 2004 3:12 am
Location: UK

Postby goldenboy » Sun Jan 03, 2010 9:02 am

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

Postby r00k » Sun Jan 03, 2010 9:40 am

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;
};
r00k
 
Posts: 1110
Joined: Sat Nov 13, 2004 10:39 pm

Next

Return to Programming Tutorials

Who is online

Users browsing this forum: No registered users and 1 guest