[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!
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.
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.
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.