Inside3D!
     

Tutorial: Rotating Brush Models for QuakeWorld
Goto page 1, 2, 3, 4, 5, 6  Next
 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> Programming Tutorials
View previous topic :: View next topic  
Author Message
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Fri Jan 01, 2010 8:54 pm    Post subject: Tutorial: Rotating Brush Models for QuakeWorld Reply with quote

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:
Code:

/*
============
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:

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:

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:

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:

   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:

            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:

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!
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Fri Jan 01, 2010 9:37 pm    Post subject: Re: Tutorial: Rotating Brush Models for QuakeWorld Reply with quote

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.
Back to top
View user's profile Send private message
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Sat Jan 02, 2010 10:47 pm    Post subject: Reply with quote

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..
Back to top
View user's profile Send private message
frag.machine



Joined: 25 Nov 2006
Posts: 728

PostPosted: Sat Jan 02, 2010 11:09 pm    Post subject: Reply with quote

Excellent tut, avirox.
_________________
frag.machine - Q2K4 Project
http://fragmachine.quakedev.com/
Back to top
View user's profile Send private message Visit poster's website
goldenboy



Joined: 05 Sep 2008
Posts: 310
Location: Kiel

PostPosted: Sat Jan 02, 2010 11:50 pm    Post subject: Reply with quote

Quote:
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:
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...
_________________
ReMakeQuake
The Realm of Blog Magic
Back to top
View user's profile Send private message
avirox



Joined: 16 Aug 2006
Posts: 109

PostPosted: Sun Jan 03, 2010 12:35 am    Post subject: Reply with quote

FTE has rotating brush ents, actually, but it looks different in code Smile Also, you need to compile your map's rotating entity with an origin brush.
Back to top
View user's profile Send private message
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Sun Jan 03, 2010 1:56 am    Post subject: Reply with quote

velocity and thus avelocity only work on MOVETYPE_PUSH entities if they have a valid nextthink.
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
goldenboy



Joined: 05 Sep 2008
Posts: 310
Location: Kiel

PostPosted: Sun Jan 03, 2010 2:37 am    Post subject: Reply with quote

Spike wrote:
velocity and thus avelocity only work on MOVETYPE_PUSH entities if they have a valid nextthink.


OK. That seems to do something Smile

Code:
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:
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.
_________________
ReMakeQuake
The Realm of Blog Magic


Last edited by goldenboy on Sun Jan 03, 2010 3:05 am; edited 1 time in total
Back to top
View user's profile Send private message
Baker



Joined: 14 Mar 2006
Posts: 1538

PostPosted: Sun Jan 03, 2010 2:45 am    Post subject: Reply with quote

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.
Back to top
View user's profile Send private message
goldenboy



Joined: 05 Sep 2008
Posts: 310
Location: Kiel

PostPosted: Sun Jan 03, 2010 3:06 am    Post subject: Reply with quote

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

2. No.
_________________
ReMakeQuake
The Realm of Blog Magic
Back to top
View user's profile Send private message
goldenboy



Joined: 05 Sep 2008
Posts: 310
Location: Kiel

PostPosted: Sun Jan 03, 2010 3:10 am    Post subject: Reply with quote

Code:
/*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/single_player/maps/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/6a6182e6/origin.html

Small vid of a happily spinning yellow block - in DP.
_________________
ReMakeQuake
The Realm of Blog Magic
Back to top
View user's profile Send private message
r00k



Joined: 13 Nov 2004
Posts: 483

PostPosted: Sun Jan 03, 2010 5:57 am    Post subject: Reply with quote

Nice!
Back to top
View user's profile Send private message
Spike



Joined: 05 Nov 2004
Posts: 944
Location: UK

PostPosted: Sun Jan 03, 2010 6:41 am    Post subject: Reply with quote

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).
_________________
What's a signature?
Back to top
View user's profile Send private message Visit poster's website
goldenboy



Joined: 05 Sep 2008
Posts: 310
Location: Kiel

PostPosted: Sun Jan 03, 2010 9:02 am    Post subject: Reply with quote

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 Smile

I tried a bit of stuff like self.nextthink = time + 0.1 and similar, but no luck.
_________________
ReMakeQuake
The Realm of Blog Magic
Back to top
View user's profile Send private message
r00k



Joined: 13 Nov 2004
Posts: 483

PostPosted: Sun Jan 03, 2010 9:40 am    Post subject: Reply with quote

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:


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;
};
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    Inside3d Forums Forum Index -> Programming Tutorials All times are GMT
Goto page 1, 2, 3, 4, 5, 6  Next
Page 1 of 6

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2004 phpBB Group