Inside3D!
     

Jumping on Exploboxes

 
Post new topic   Reply to topic    Inside3d Forums Forum Index -> Programming Tutorials
View previous topic :: View next topic  
Author Message
mh



Joined: 12 Jan 2008
Posts: 909

PostPosted: Fri Aug 14, 2009 12:18 am    Post subject: Jumping on Exploboxes Reply with quote

EDIT - put begin and end comments around the relevant code changes so that you can more easily see the bits that you need to add to your engine if you don't want to just copy/paste whole functions

This came to light after everyone started playing the Bloody Slipgates mod. There is a place where one of the red keys is hidden where there are 3 routes to it. Route 1 involves a fairly fiendish ninja jump (captured for posterity in my demo), route 2 involves a clip brush in a place where it doesn't seem obvious, and route 3 involves jumping off a small explobox. Now, most if not all engines aside from DP don't support jumping off exploboxes, so route 2 was added at the last minute in order to provide a way (I think the mod author may not have realised that route 1 was possible here).

Here I'm going to show you how to implement the ability to jump off exploboxes in Quake. Before I start though there's a big disclaimer to make.

BIG DISCLAIMER

This is a gameplay changing tutorial. It might make some areas in some maps that were previously inaccessible easier to get at, so I consider it a "cheating" change. It's all server-side though so it won't affect MP games, but just be aware that if you implement it you may spoil your enjoyment of some maps or mods.

I've only tested it on Bloody Slipgates, and only in the relevant area. I've also ensured that it doesn't interfere with other regular brush entities.

Some mods may do things with exploboxes that break as a result of this code. You have been warned.


Now, open sv_phys.c and look for the SV_Physics function. Replace it with this:
Code:
void SV_Physics (void)
{
   int      i;
   edict_t   *ent;

// let the progs know that a new frame has started
   pr_global_struct->self = EDICT_TO_PROG(sv.edicts);
   pr_global_struct->other = EDICT_TO_PROG(sv.edicts);
   pr_global_struct->time = sv.time;
   PR_ExecuteProgram (pr_global_struct->StartFrame);

//SV_CheckAllEnts ();

//
// treat each object in turn
//
   ent = sv.edicts;
   for (i=0 ; i<sv.num_edicts ; i++, ent = NEXT_EDICT(ent))
   {
      // jumping on exploboxes begin
      qboolean modelhack = false;
      // jumping on exploboxes end

      if (ent->free)
         continue;

      // jumping on exploboxes begin
      // don't do the world edict
      if (ent->v.touch == 0 && i > 0)
      {
         model_t *mod = sv.models[(int) ent->v.modelindex];

         if (mod)
         {
            if (mod->type == mod_brush && mod->name[0] != '*')
            {
               // we have a brushmodel that (1) doesn't have a touch function, and (2)
               // is an instanced BSP model, so we switch the solid and movetype
               ent->v.solid = SOLID_BSP;
               ent->v.movetype = MOVETYPE_PUSH;

               // flag that we're hacking the model
               modelhack = true;
            }
         }
      }
      // jumping on exploboxes end

      if (pr_global_struct->force_retouch)
      {
         SV_LinkEdict (ent, true);   // force retouch even for stationary
      }

      if (i > 0 && i <= svs.maxclients)
         SV_Physics_Client (ent, i);
      else if (ent->v.movetype == MOVETYPE_PUSH)
         // jumping on exploboxes begin
         SV_Physics_Pusher (ent, modelhack);
         // jumping on exploboxes end
      else if (ent->v.movetype == MOVETYPE_NONE)
         SV_Physics_None (ent);
#ifdef QUAKE2
      else if (ent->v.movetype == MOVETYPE_FOLLOW)
         SV_Physics_Follow (ent);
#endif
      else if (ent->v.movetype == MOVETYPE_NOCLIP)
         SV_Physics_Noclip (ent);
      else if (ent->v.movetype == MOVETYPE_STEP)
         SV_Physics_Step (ent);
      else if (ent->v.movetype == MOVETYPE_TOSS
      || ent->v.movetype == MOVETYPE_BOUNCE
#ifdef QUAKE2
      || ent->v.movetype == MOVETYPE_BOUNCEMISSILE
#endif
      || ent->v.movetype == MOVETYPE_FLY
      || ent->v.movetype == MOVETYPE_FLYMISSILE)
         SV_Physics_Toss (ent);
      else
         Sys_Error ("SV_Physics: bad movetype %i", (int)ent->v.movetype);         
   }
   
   if (pr_global_struct->force_retouch)
      pr_global_struct->force_retouch--;   

   sv.time += host_frametime;
}


The cause of not being able to jump off an explobox is because of the movetype value that they have (MOVETYPE_NONE) so here we detect if we have an explobox and switch the movetype. We also need to switch the solid value because MOVETYPE_PUSH requires SOLID_BSP.

The code to detect if it's an explobox can work with any instanced BSP model. First it detects if the model has a touch function; if it does we assume that it''s meant to be interacted with in some way, and so we don't switch it. Then we ensure that we've got an instanced BSP model. Note also the change to the call to SV_Physics_Pusher in here. I'll talk about that one after the next block of code.

Next, we replace SV_Physics_Pusher itself with this:
Code:
void SV_Physics_Pusher (edict_t *ent, qboolean modelhack)
{
   float   thinktime;
   float   oldltime;
   float   movetime;

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

   if (movetime)
   {
#ifdef QUAKE2
      if (ent->v.avelocity[0] || ent->v.avelocity[1] || ent->v.avelocity[2])
         SV_PushRotate (ent, movetime);
      else
#endif
         SV_PushMove (ent, movetime);   // advances ent->v.ltime if not blocked
   }

   // jumping on exploboxes begin
   if (modelhack)
   {
      // run regular thinking
      SV_RunThink (ent);
   }
   else if (thinktime > oldltime && thinktime <= ent->v.ltime)
   // jumping on exploboxes end
   {
      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;
   }

}


All that we're doing here is detecting if the model was hacked above and sending it through the think function for a MOVETYPE_NONE model if so.

This code as it stands will potentially give you a "SOLID_BSP with a non-BSP model" error so the last thing to do is fix that. I'm going to do a quick and dirty fix rather than something more robust, so open world.c and replace SV_HullForEntity with this:
Code:
hull_t *SV_HullForEntity (edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset)
{
   model_t      *model;
   vec3_t      size;
   vec3_t      hullmins, hullmaxs;
   hull_t      *hull;

// decide which clipping hull to use, based on the size
   if (ent->v.solid == SOLID_BSP)
   {   // explicit hulls in the BSP model
      if (ent->v.movetype != MOVETYPE_PUSH)
         Sys_Error ("SOLID_BSP without MOVETYPE_PUSH");

      model = sv.models[ (int)ent->v.modelindex ];

      // jumping on exploboxes begin
      if (!model || model->type != mod_brush)
         goto dont_crash;
      // jumping on exploboxes end

      VectorSubtract (maxs, mins, size);
      if (size[0] < 3)
         hull = &model->hulls[0];
      else if (size[0] <= 32)
         hull = &model->hulls[1];
      else
         hull = &model->hulls[2];

// calculate an offset value to center the origin
      VectorSubtract (hull->clip_mins, mins, offset);
      VectorAdd (offset, ent->v.origin, offset);
   }
   else
   {   // create a temp hull from bounding box sizes
dont_crash:
      VectorSubtract (ent->v.mins, maxs, hullmins);
      VectorSubtract (ent->v.maxs, mins, hullmaxs);
      hull = SV_HullForBox (hullmins, hullmaxs);
      
      VectorCopy (ent->v.origin, offset);
   }


   return hull;
}


This happens because the entities model changes from a brush to a sprite when it explodes, so all we do here is jump to the non-SOLID_BSP hull code instead of throwing a Sys_Error.

Now, this code is fairly hacky and there are better ways of doing the whole thing; one possible solution would be to store the original values of solid and movetype in the edict_t struct and switch between them and the new ones as required. I did it this way so that I could present the tutorial in a manner that's easier to follow, but I wouldn't claim is the right way, or even a good way. But if you want to jump off exploboxes it'll let you do it. Any improvements are entirely up to you.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines


Last edited by mh on Sat Aug 15, 2009 2:56 pm; edited 1 time in total
Back to top
View user's profile Send private message Visit poster's website
Team Xlink



Joined: 25 Jun 2009
Posts: 320

PostPosted: Fri Aug 14, 2009 1:38 am    Post subject: Reply with quote

Nice Tutorial!

Finally, I can take the fun route!

Thank you!
_________________
Anonymous wrote:
if it works, it works. if it doesn't, HAHAHA!
Back to top
View user's profile Send private message
Downsider



Joined: 16 Sep 2008
Posts: 478

PostPosted: Fri Aug 14, 2009 4:09 pm    Post subject: Reply with quote

Good job coming up with the fix, but I hate seeing tutorials that just say, "Replace this function with this function because I said so".

At the very least, you could compare the two functions to show the differences, so that way you could work out what's actually going on. Some people are working with different code bases that have changed certain aspects of the engine and can't simply replace the entire function. Others also want to understand what's going on, too, especially since a lot of these additions are very hacky and aren't understood immediately unless your knowledge of Quake is rather deep.
Back to top
View user's profile Send private message
c0burn



Joined: 05 Nov 2004
Posts: 158
Location: Liverpool, England

PostPosted: Fri Aug 14, 2009 7:06 pm    Post subject: Reply with quote

Can this be fixed in QC the same way the URQP fixes sliding when you jump on a monster? (touch function that fiddles with FL_ONGROUND)?
Back to top
View user's profile Send private message Visit poster's website AIM Address MSN Messenger
mh



Joined: 12 Jan 2008
Posts: 909

PostPosted: Sat Aug 15, 2009 2:52 pm    Post subject: Reply with quote

Downsider wrote:
Good job coming up with the fix, but I hate seeing tutorials that just say, "Replace this function with this function because I said so".

At the very least, you could compare the two functions to show the differences, so that way you could work out what's actually going on. Some people are working with different code bases that have changed certain aspects of the engine and can't simply replace the entire function. Others also want to understand what's going on, too, especially since a lot of these additions are very hacky and aren't understood immediately unless your knowledge of Quake is rather deep.

I have commented the relevant code areas as well as provided some explanation text, but I can amend the tutorial and put // begin and // end comments around the modified parts. In the specific case of this one, the physics code is something that I think not too many people modify, but I suppose you're right all the same.

c0burn wrote:
Can this be fixed in QC the same way the URQP fixes sliding when you jump on a monster? (touch function that fiddles with FL_ONGROUND)?

Should be possible, yes. I'm not too familiar with QC but it's all fields that are exported through ent->v so they should also be accessible from QC. In fact I would be of the opinion that QC is the correct place to do it. Very Happy
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines
Back to top
View user's profile Send private message Visit poster's website
Lardarse



Joined: 05 Nov 2005
Posts: 243
Location: Bristol, UK

PostPosted: Sun Aug 16, 2009 8:59 pm    Post subject: Re: Jumping on Exploboxes Reply with quote

mh wrote:
This came to light after everyone started playing the Bloody Slipgates mod. There is a place where one of the red keys is hidden where there are 3 routes to it. Route 1 involves a fairly fiendish ninja jump (captured for posterity in my demo), route 2 involves a clip brush in a place where it doesn't seem obvious, and route 3 involves jumping off a small explobox. Now, most if not all engines aside from DP don't support jumping off exploboxes, so route 2 was added at the last minute in order to provide a way (I think the mod author may not have realised that route 1 was possible here).

I couldn't do it. negke tried to do it in his first run demo and couldn't do it either...

There is actually a 4th method: Look at the box, jump, shoot the box.

I would like a QC solution, though...
_________________
<ekiM> Son, you're writing data structures your CPU can't cache.
Back to top
View user's profile Send private message
mh



Joined: 12 Jan 2008
Posts: 909

PostPosted: Sun Aug 16, 2009 10:24 pm    Post subject: Re: Jumping on Exploboxes Reply with quote

Lardarse wrote:
I couldn't do it. negke tried to do it in his first run demo and couldn't do it either...

Well if I could do it, it can't be that hard. Laughing
Lardarse wrote:
I would like a QC solution, though...

Obviously the ideal way to go; one of our resident QC hackers would have to help you with that, however. I'm still highly dubious about ever having posted this thing; it's just Wrong and Bad in so many ways (cheating, edict behaviour modification, physics hacking, etc).
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines
Back to top
View user's profile Send private message Visit poster's website
Downsider



Joined: 16 Sep 2008
Posts: 478

PostPosted: Thu Aug 20, 2009 9:01 pm    Post subject: Reply with quote

Kurok had a fix for this from version 0.3 -> 0.4, if I remember correctly. Ask MDave, although for all I know he could have changed the exploboxes into brushes or something (Is this possible?). Or maybe I'm totally off my hat and it never was fixed, or it's a different problem entirely! Laughing
Back to top
View user's profile Send private message
r00k



Joined: 13 Nov 2004
Posts: 483

PostPosted: Fri Aug 21, 2009 5:40 pm    Post subject: Reply with quote

c0burn wrote:
Can this be fixed in QC the same way the URQP fixes sliding when you jump on a monster? (touch function that fiddles with FL_ONGROUND)?


Maybe if touching an explosion box, switch it's movetype to MOVETYPE_STEP, and keeping track of that entity, so if not touching it, switch it back to MOVETYPE_NONE... i'm still looking for an explosion box in normal Quake maps that i can stand on.. e3m1? I think as c0burn mentioned in player_touch setting the box ONGROUND would keep u from slip-sliding around while on it, fiddling with the movetype should allow you to jump off it... I'll have to play with this as it might breakif u blow it up while standing on it Neutral

hmm weird. I havent looked in misc.qc in AT LEAST 6 years...
here's what i found in my CAx mod...
Code:

void() misc_explobox2 =
{
   remove(self);
   return;

   local float   oldz;
   
   self.solid = SOLID_BBOX;
   //self.movetype = MOVETYPE_NONE;
   self.movetype = MOVETYPE_PUSH;
   precache_model2 ("maps/b_exbox2.bsp");
   setmodel (self, "maps/b_exbox2.bsp");
   precache_sound ("weapons/r_exp3.wav");
   self.health = 20;
   self.th_die = barrel_explode;
   self.takedamage = DAMAGE_AIM;

   self.origin_z = self.origin_z + 2;
   oldz = self.origin_z;
   droptofloor(0,0);
   if (oldz - self.origin_z > 250)
   {
      dprint ("item fell out of level at ");
      dprint (vtos(self.origin));
      dprint ("\n");
      remove(self);
   }
};


I dont remember switching that movetype before nor do i know the result as in CA i clear this entity out. When I get home I'll have to test this as I cant compile under this OS (i have a 16gb thumbdrive with my quake stuff that i take along with me Wink )
Back to top
View user's profile Send private message
mh



Joined: 12 Jan 2008
Posts: 909

PostPosted: Fri Aug 21, 2009 6:22 pm    Post subject: Reply with quote

You'll need to switch to SOLID_BSP as well, but I do believe that is all that's required. Just hacked it into a vanilla ID QC and it worked fine.

There's a box you can test it on in e4m1 by the way, in the room with the lasers.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines
Back to top
View user's profile Send private message Visit poster's website
Wazat



Joined: 15 Oct 2004
Posts: 732
Location: Middle 'o the desert, USA

PostPosted: Sat Aug 22, 2009 2:59 am    Post subject: Reply with quote

Assuming my memories weren't planted by conspiring hamsters (haaamsteeers!), I'm reasonably certain I had walking & jumping off of non-BSP objects working perfectly in one of my mods. Thus I was able to run around on monsters' heads and jump off.

Again, if memory serves me, it was a simple feat of tracelining down in playerprethink, testing if the impacted non-bsp object was worthy of your footsteps (most things are: monsters, explode boxes, maybe even triggers), and setting the player's FL_ONGROUND flag. Once the flag was set, the player could walk as though he was on a SOLID_BSP object.

But I still suspect conspiring hamsters planting false memories to keep me under control.


It can be fun abusing the power of QC like this to get great results. I'm still proud of the semi-solid-corpses-without-an-engine-mod hack I put in Ace of Nails. And Conquest's pause-the-world-without-pausing feature.[/shamelessbrag]
_________________
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.
Back to top
View user's profile Send private message MSN Messenger
Team Xlink



Joined: 25 Jun 2009
Posts: 320

PostPosted: Sat Dec 05, 2009 9:05 pm    Post subject: Reply with quote

mh wrote:
[b]
Next, we replace SV_Physics_Pusher itself with this:
Code:
void SV_Physics_Pusher (edict_t *ent, qboolean modelhack)
{
   float   thinktime;
   float   oldltime;
   float   movetime;
}



A friend of mine was having trouble with that and I told him about this so I thought maybe other people were having trouble.

The part where you add the qboolean modelhack wasn't commented and he didn't notice it so maybe a comment there might help others as well.

EDIT: I am confused. How exactly is it supposed to work?

I tried taking that way but it didn't change anything at all.

I think I ama misunderstanding how to use it.
_________________
Anonymous wrote:
if it works, it works. if it doesn't, HAHAHA!
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
Page 1 of 1

 
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