Quake 2 is another classic from id software.While not modded quite as much as it's predecessor or Q3A, it is still a blast to play and the mod's on offer are bundles of fun. Just step into Quake 2 Rocket Arena, armed with the new and almighty rocket and rail gun and you will be gibbing for hours on end.If you dig fast paced action, then load up the good ole Quake 2, fire up a mod or two and have some fun!

Post tutorial Report RSS Climbing - Hard to reach places?

This tutorial will show you how to add climbing support in your Quake2 mod. With just a few presses of a button and holding +forward, you're on your way to new heights!

Posted by on - Basic Server Side Coding

[page=Intro]
This tutorial will show you how to add climbing support in your Quake2 mod. With just a few presses of a button and holding +forward, you're on your way to new heights!

This tutorial is basic/intermediate. It may take a while to get used to though. There are very small things to add for this big feature! Kudos for Mr_Grim for the great mod Dirty for releasing his source!
[page=Doing the code]
Alright, here we go!

First, let's add

//AQC - Climbing
        qboolean        hanging;
        vec3_t          hang_point;
        float           flip_time;
        //end AQC

to your g_clients struct in G_local.h, which starts with:

// this structure is cleared on each PutClientInServer(),
// except for 'client->pers'
struct gclient_s
{

That's done!

Now, let's get into the beef and meat.

Head over to g_misc, and add this at the bottom after the bracket:

qboolean Grab_n_Climb (edict_t *ent) //AQC
{
        vec3_t  forward, start, end;
        int     check = 0;
        int     plusup = 0;
        trace_t tr;

        if (ent->deadflag || (ent->movetype == MOVETYPE_NOCLIP))
                return false;

        if (ent->groundentity)
                return false;

        AngleVectors (ent->s.angles, forward, NULL, NULL);
        //forward[2] = 0;
        VectorNormalize (forward);

        VectorCopy (ent->s.origin, start);
        // Space above?
        start[2] += 56;
        tr = gi.trace (ent->s.origin, NULL, NULL, start, ent, MASK_SHOT);
        if (tr.fraction < 1.0)
                return false;

        VectorCopy (ent->s.origin, start);

        while (check < 64)
        {
                start[2] += 1;
                VectorMA (start, 40, forward, end); // Increased from 24
                tr = gi.trace (start, NULL, NULL, end, ent, MASK_SOLID);
                if (tr.fraction < 1.0)
                        check = 64;
                else
                        check++;
        }

        if (tr.fraction < 1.0)
        {
                VectorCopy (ent->s.origin, start);
                start[2] += 84;
                VectorMA (start, 40, forward, end);  // Increased from 24
                tr = gi.trace (start, NULL, NULL, end, ent, MASK_SOLID);

                if (!(tr.fraction < 1.0))
                {
                        VectorCopy (end, ent->client->hang_point);
                        check = (260 - plusup);
                        if (ent->velocity[2] < 0)
                                ent->velocity[2] = 0;
                        ent->velocity[2] += check;
                        if (ent->velocity[2] > 400)
                                ent->velocity[2] = 400;

                        //gi.dprintf ("Grab_n_Climb - pass\n");
                        return true;
                }
        }
        //gi.dprintf ("Grab_n_Climb - fail\n");
        return false;
}

qboolean HangingCheck (edict_t *ent)
{
        vec3_t  forward, start, end;
        int     check = 0;
        trace_t tr;

        if (ent->groundentity)
        {
                ent->client->hanging = false;
                return false;
        }

        AngleVectors (ent->s.angles, forward, NULL, NULL);
        //forward[2] = 0;
        VectorNormalize (forward);

        VectorCopy (ent->s.origin, start);
        // Space above?
        start[2] += 56;
        tr = gi.trace (ent->s.origin, NULL, NULL, start, ent, MASK_SHOT);
        if (tr.fraction < 1.0)
        {
                ent->client->hanging = false;
                return false;
        }
        VectorCopy (ent->s.origin, start);
        start[2] -= 32;

        while (check < 88)
        {
                start[2] += 1;
                VectorMA (start, 40, forward, end); // Increased from 24
                tr = gi.trace (start, NULL, NULL, end, ent, MASK_SOLID);
                if (tr.fraction < 1.0)
                        check = 88;
                else
                        check++;
        }

        if (tr.fraction < 1.0)
        {
                VectorCopy (ent->s.origin, start);
                start[2] = ent->client->hang_point[2];
                VectorMA (start, 40, forward, end);  // Increased from 24
                tr = gi.trace (start, NULL, NULL, end, ent, MASK_SOLID);

                if (!(tr.fraction < 1.0))
                {
                        ent->client->hanging = true;
                        if (ent->velocity[2] > 400)
                                ent->velocity[2] = 400;
                        return true;
                }
        }
        ent->client->hanging = false;
        return false;
}

qboolean CheckHang (edict_t *ent)
{
        vec3_t  forward, start, end;
        int     check;
        trace_t tr;

        //gi.dprintf ("CheckHang - ");
        if (ent->groundentity)
        {
                //gi.dprintf ("on ground, fail\n");
                return false;
        }
        AngleVectors (ent->s.angles, forward, NULL, NULL);
        forward[2] = 0;
        VectorNormalize (forward);

        VectorCopy (ent->s.origin, start);

        start[2] += 52;

        // Space above?
        tr = gi.trace (ent->s.origin, NULL, NULL, start, ent, MASK_SHOT);
        if (tr.fraction < 1.0)
        {
                //gi.dprintf ("something above, fail\n");
                return false;
        }

        check = 0;
        while (check < 12)
        {
                VectorMA (start, 24, forward, end);
                tr = gi.trace (start, NULL, NULL, end, ent, MASK_SHOT);
                if (tr.fraction < 1.0)
                        check = 12;
                else
                {
                        start[2] += 1;
                        check++;
                }
        }

        if (tr.fraction < 1.0)
        {
                //gi.dprintf ("fp pass, ");
                check = 0;
                while (check < 12)
                {
                        VectorMA (start, 24, forward, end);
                        tr = gi.trace (start, NULL, NULL, end, ent, MASK_SHOT);

                        if (!(tr.fraction < 1.0))
                        {
                                ent->client->hanging = true;
                                VectorCopy (tr.endpos, ent->client->hang_point);
                                //gi.dprintf ("full pass\n");
                                return true;
                        }
                        else 
                                check++;
                        start[2] += 1;
                }
        }

        //gi.dprintf ("fail\n");
        ent->client->hanging = false;
        return false;
} // AQC END

Big code! Now, go climb those walls and get to those hard to reach places, and pick them off with your sniper!

[page=Conclusion/Credits]

I would like to thank the creator of Dirty, Mr_Grim, for making the Action Quake2 remake. Also, he recently released his source (Because I asked him to for one, and that he got lots of emails asking for it), so I looked through it and added his climbing code to my mod and it worked, so I thought I'd share it with you!

Dirty's new site is located at Brazen.planetquake.gamespy.com
He currently has nothing uploaded, but I am sure he will upload more soon!

Post comment Comments
farbin
farbin - - 3 comments

Hmm, more great code ideas, sadly, this tutorial is missing a few important lines of code. So, I had to go out and get the source for DirtyQ2 and find what was missing.

There needs to be a way to activate the climbing routines when you want to climb.

1.) In g_misc.c, the following should be added:

after the end of this function ...

void ThrowClientHead (edict_t *self, int damage)

add these 2 functions ...

/*
==============
Cmd_Action_On
==============
*/
qboolean Grab_n_Climb (edict_t *ent);

void Cmd_Action_On (edict_t *ent)
{
if (ent->deadflag || (ent->movetype == MOVETYPE_NOCLIP))
return;

// If weapon idle...
if (ent->client->weaponstate == WEAPON_READY)
{
// If jumping/falling etc
if (Grab_n_Climb (ent))
{
//ent->client->hanging = true;
return;
}
}
// On
ent->client->pers.grabbing = 1;
}

/*
==============
Cmd_Action_Off
==============
*/
void Cmd_Action_Off (edict_t *ent)
{
if (ent->client->pers.grabbing != 2)
// Off
ent->client->pers.grabbing = -1;
}

2.) Now, you will have to add the following to g_local.h:

in client_persistant_t (somewhere at the end) add ...

int grabbing;

and in struct gclient_s (again, somewhere near the end) add these:

qboolean hanging;
vec3_t hang_point;
float flip_time;

3.) Now, in g_cmds.c you need to add the commands to activate the climbing (the Action_On and Action_Off added previously). So,at the end of void ClientCommand (edict_t *ent), add these:

before ...

else // anything that doesn't match a command will be a chat
Cmd_Say_f (ent, false, true);

add ...

else if (Q_stricmp(cmd, "action_on") == 0)
Cmd_Action_On(ent);
else if (Q_stricmp(cmd, "action_off") == 0)
Cmd_Action_Off(ent);

4.) And the last peice of this would be to add your binds to your .cfg file (just to make life easier). For example:

add ...
alias action "action_on"
alias action_on "action_on;alias action "action_off"
alias action_off "action_off;alias action "action_on"
bind o "action"

or whatever key you which to use.

Additionally, you can fiddle with the value in the line

check = (260 - plusup);
(located in qboolean Grab_n_Climb (edict_t *ent))

to make your player climb faster/slower (more/less force).

farbin

p.s. Sorry, not trying to be a jerk, it just irritates me when a tutorial doesn't work or parts are missing. On the other hand, it makes me actually look at the source and not just copy-n-paste so, I learn a lot more.

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

You don't need those. That block is for the +action, from Dirty.
You can just press the cmd I added to be able to climb up.
All it does is check for a location and up your velocity..
Works fine for me, it was implemented into my oldest AQC mod.

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

Note that you have to make a cmd yourself, making it activate "Grab_n_Climb".

else if (Q_stricmp(cmd, "climb") == 0)
Grab_n_Climb(ent);

Taken from AQC.

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: