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: