Post tutorial Report RSS Push and Pull

Ever wanted a tractor beam in your Quake2 mod? Ever wanted to just push your enemies around like a ragdoll? Does that lava down there look tempting? A better way to kill, push them in, or pull em towards you! Fun and great for CTF teamming on the flag!

Posted by on - Basic Client Side Coding

[page=Introduction]
Alright, this tutorial is very easy. Just follow my steps, and you'll be fine.

Again with my little statement. I think you should always make your own files for each function you add, because it makes it easy to edit.

[page=Adding The Command]
First, we'll start by the last thing.. I know it's odd, but incase we forget we might as well do this first.

Find:

else if (Q_stricmp (cmd, "wave") == 0)
         Cmd_Wave_f (ent);

Under it, add these two:

// Paril - Added for the Push/Pull
     else if (Q_stricmp (cmd, "push") == 0)
         Cmd_Push_f (ent);
     else if (Q_stricmp (cmd, "pull") == 0)
         Cmd_Pull_f (ent);

Good. Now, to the meat and bones of the command: The trace!

[page=Cmd_Push function]
Let's start by adding the Cmd_Push_f function. Find the function Cmd_Wave_f, and when it ends, let's start adding:

/*
=================
Cmd_Push_f
Added by Paril for Push/Pull
=================
*/
void Cmd_Push_f (edict_t *ent)
{
 vec3_t  start;
 vec3_t  forward;
 vec3_t  end;
 trace_t tr;

 VectorCopy(ent->s.origin, start); // Copy your location
 start[2] += ent->viewheight; // vector for start is at your height of view
 AngleVectors(ent->client->v_angle, forward, NULL, NULL); // Angles
 VectorMA(start, 8192, forward, end); // How far will the line go?
 tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT); // Trace the line
 if ( tr.ent && ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) ) // Trace the line
 {
        VectorScale(forward, 5000, forward); //Where to hit? Edit 5000 to whatever you like the push to be
        VectorAdd(forward, tr.ent->velocity, tr.ent->velocity); // Adding velocity vectors
 }
}

Not hard is it? Cmd_Pull_f is the same pretty much. Let's check it out!

[page=Cmd_Pull]
Now, let's pull your enemies into that dazzling lava!

/*
=================
Cmd_Pull_f
Added by Paril for Push/Pull
=================
*/
void Cmd_Pull_f (edict_t *ent)
{
 vec3_t  start;
 vec3_t  forward;
 vec3_t  end;
 trace_t tr;

 VectorCopy(ent->s.origin, start); // Copy your location
 start[2] += ent->viewheight; // vector for start is at your height of view
 AngleVectors(ent->client->v_angle, forward, NULL, NULL); // Angles
 VectorMA(start, 8192, forward, end); // How far will the line go?
 tr = gi.trace(start, NULL, NULL, end, ent, MASK_SHOT); // Trace the line
 if ( tr.ent && ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) ) // Trace the line
 {
        VectorScale(forward, -5000, forward); //Where to hit? Edit -5000 to whatever you like the push to be
        VectorAdd(forward, tr.ent->velocity, tr.ent->velocity); // Adding velocity vectors
 }
}

It's EXACTLY the same, but pulling -5000 instead of pushing 5000.

Not a hard concept, but can be used for good.

[page=Modifications to the Push and Pull]
You can change the Push/Pull for more of an effect, say if you want it to send a sound for when it's transmitted, add this after the trace of the line (make sure to change both):

gi.sound (ent, CHAN_AUTO, gi.soundindex ("items/damage2.wav"), 1, ATTN_NORM, 0);

Also, this can be altered to spawn a laser when the trace is spawned by adding this after the VectorAdd:

// set origin of laser beam
      VectorMA (start, 8192, forward, end);
      VectorCopy (ent->s.origin, start);
      // trace for end point of laser beam.
      // the laser aim is perfect. 
      tr = gi.trace (from, NULL, NULL, end, self, MASK_SHOT);      
      // send laser beam temp entity to clients
      VectorCopy (tr.endpos, from);
      gi.WriteByte (svc_temp_entity);
      gi.WriteByte (TE_BFG_LASER);      
      gi.WritePosition (start);
      gi.WritePosition (tr.endpos);
      gi.multicast (ent->s.origin, MULTICAST_PHS);

I haven't tested the laser part, but play around with it and see how it works.

Many things can be added, like an ammo requirement for example, by adding this at the beginning:

if (ent->client->pers.inventory[ITEM_INDEX(Finditem ("Cells"))] <= 9) // requires 10 cells
{
    gi.cprintf (ent, PRINT_HIGH, "You need 10 cells to use Push or Pull\n"); // Notify them
    return; // Stop the command from going
}

Now, after the VectorAdd, add:

ent->client->pers.inventory[ITEM_INDEX(Finditem ("Cells"))] -= 10;

Anyways, this Push/Pull can be the base of a tractor beam weapon if you wanted it to be.
That's all for now..

Paril('s Uber Cool Tutorials) ;)

Post comment Comments
farbin
farbin - - 3 comments

Careful!
Both of your functions for Push and Pull are named void Cmd_Push_f (edict_t *ent), you just changed the VectorScale value.
error C2084: function 'void __cdecl Cmd_Push_f(struct edict_s *)' already has a body

And, no, your laser code does not work:
tr = gi.trace (from, NULL, NULL, end, self, MASK_SHOT);
error C2065: 'from' : undeclared identifier
error C2065: 'self' : undeclared identifier
error C2109: subscript requires array or pointer type
error C2198: 'gi' : too few actual parameters

As for the ammo requirment:
error C2113: pointer can only be subtracted from another pointer.

So, if you don't add any of the additional modifications, the Push/Pull functions work fine.

farbin

Reply Good karma Bad karma+1 vote
Paril Author
Paril - - 24 comments

Sorry, that's a very old tutorial.
I'll fix it right now.

Reply Good karma+1 vote
Paril Author
Paril - - 24 comments

As for the ammo requirment:
error C2113: pointer can only be subtracted from another pointer.

Not sure where that came from....

I'll check about the laser.

Reply Good karma+1 vote
Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: