q2wf-portable/grapple.c

617 lines
17 KiB
C
Raw Permalink Normal View History

/*****************************************************************
Grapple source code - by Acrid-, acridcola@hotmail.com
..............................................................
This file is Copyright(c) 1999, Acrid-, All Rights Reserved.
..............................................................
Should you decide to release a modified version of the Grapple, you
MUST include the following text (minus the BEGIN and END lines) in
the documentation for your modification, and also on all web pages
related to your modification, should they exist.
--- BEGIN ---
The Grapple and related code ,minus the pushcode or lines marked tut,
are a product of Acrid- designed for Weapons Factory, and is available as
part of the Weapons Factory Source Code or a seperate tutorial.
This program MUST NOT be sold in ANY form. If you have paid for
this product, you should contact Acrid- at:
acridcola@hotmail.com
--- END ---
have fun,
Acrid-
*****************************************************************/
//#define USE_OLD_GRAPPLE 1
/*
==============================
Grapple Code by Acrid
4/6/99
code in other files: noted by newgrap 4/99
g_cmds.c
g_items.c
g_misc.c
g_phys.c
p_client.c
wf_local_client.h
ctf.h
I looked a few tutors but they never quite stopped bouncing on my system.
This code stops all bounce by turning off gravity and changing velocity
to 100 when length is 64.
It also uses pusher code from a tutorial I found to correct bounce when
grappled to objects like doors,plats etc.
Plus you can use this code for a normal ctfsytle grapple,a offhand
ctfsytle grapple and a ctfsytle weapon grapple.
WF notes:
bind key +grapple for offhand
bind key grapple for normal
use grapple for weapon_grapple
by using BUTTON_USE offhand alias will no longer get reversed.
==============================
*/
#include "g_local.h"
#define DELAY_TIME 5000
void CTFFireGrapple (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect);
void CTFGrappleFire (edict_t *ent, vec3_t g_offset, int damage, int effect);
void P_ProjectSource (gclient_t *client, vec3_t point, vec3_t distance, vec3_t forward, vec3_t right, vec3_t result);
qboolean Ended_Grappling (gclient_t *client)
{
return (!(client->buttons & BUTTON_USE) && client->oldbuttons & BUTTON_USE);
}
qboolean Is_Grappling (gclient_t *client)
{
return (client->ctf_grapple == NULL) ? false : true;
}
void CTFGrappleTouch2 (edict_t *self, edict_t *other, cplane_t *plane, csurface_t *surf)
{
float volume = 1.0;
// Release if hitting its owner
if (other == self->owner)
return;
//tut s
if (!Is_Grappling(self->owner->client) && self->health == 0)
{ botDebugPrint("!isgrap\n");
return;
}// tut e
self->health = 0;//tut
if (self->owner->client->ctf_grapplestate != CTF_GRAPPLE_STATE_FLY)
return;
if (surf && surf->flags & SURF_SKY)
{
botDebugPrint("reset\n");
CTFPlayerResetGrapple2(self->owner);
return;
}
// VectorCopy(vec3_origin, self->velocity);
PlayerNoise(self->owner, self->s.origin, PNOISE_IMPACT);
if (other != g_edicts && other->clipmask == MASK_SHOT)//tut
return;//tut
if (other->takedamage || other->client)
{ //ERASER ADDED || other->client
T_Damage (other, self, self->owner, self->velocity, self->s.origin, plane->normal, self->dmg, 1, 0, MOD_GRAPPLE);
CTFResetGrapple2(self);
return;
}
self->owner->client->ctf_grapplestate = CTF_GRAPPLE_STATE_PULL; // we're on hook
self->enemy = other;
self->solid = SOLID_NOT;
if (self->owner->client->silencer_shots)
volume = 0.2;
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grpull.wav"), volume, ATTN_NORM, 0);
gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhit.wav"), volume, ATTN_NORM, 0);
gi.WriteByte (svc_temp_entity);
gi.WriteByte (TE_SPARKS);
gi.WritePosition (self->s.origin);
if (!plane)
gi.WriteDir (vec3_origin);
else
gi.WriteDir (plane->normal);
gi.multicast (self->s.origin, MULTICAST_PVS);
//end zoid
//Pusher code
if (other != g_edicts && other->inuse && (other->movetype == MOVETYPE_PUSH ||
other->movetype == MOVETYPE_STOP))
{
other->mynoise2 = self;
self->owner->client->hook_touch = other;
// self->enemy = other;
self->groundentity = NULL;
self->flags |= FL_TEAMSLAVE;
}
VectorClear(self->velocity);
VectorClear(self->avelocity);
self->touch = NULL;
self->movetype = MOVETYPE_NONE;
self->delay = level.time + DELAY_TIME;
self->owner->client->on_hook = true;
self->owner->groundentity = NULL;
CTFGrapplePull2(self->owner);
}
void CTFGrappleThink2( edict_t *self )
{
if (!self->owner || !self->owner->client)
{
G_FreeEdict(self);
return;
}
if ((self->owner->health <= 0) || (self->owner->client->ctf_grapple != self))
{
gclient_t *cl;
cl = self->owner->client;
if (cl->ctf_grapple == self)
{
cl->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; //sever codefix test
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grreset.wav"), 1, ATTN_NORM, 0);
cl->ctf_grapple = NULL;
cl->ctf_grapplereleasetime = level.time;
cl->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
}
G_FreeEdict(self);
return;
}
//start of tut
if (level.time > self->delay)
self->prethink = CTFPlayerResetGrapple2;
else
{
if (self->owner->client->hook_touch)
{
edict_t *obj = self->owner->client->hook_touch;
if (obj == g_edicts)
{
CTFPlayerResetGrapple2(self->owner);
botDebugPrint("resetting 1\n");
return;
}
if (obj->inuse == false)
{
CTFPlayerResetGrapple2(self->owner);
botDebugPrint("resetting 2\n");
return;
}
if (obj->deadflag == DEAD_DEAD)
{
CTFPlayerResetGrapple2(self->owner);
botDebugPrint("resetting 3\n");
return;
}
}
self->nextthink = level.time + FRAMETIME;
}
}
void CTFGrappleDrawCable2 (edict_t *self)
{
vec3_t offset, start, end, f, r;
vec3_t dir;
float distance;
AngleVectors (self->owner->client->v_angle, f, r, NULL);
VectorSet(offset, 16, 16, self->owner->viewheight-8);
P_ProjectSource (self->owner->client, self->owner->s.origin, offset, f, r, start);
VectorSubtract(start, self->owner->s.origin, offset);
VectorSubtract (start, self->s.origin, dir);
distance = VectorLength(dir);
// don't draw cable if close
if (distance < 64)
return;
#if 0
if (distance > 256)
return;
// check for min/max pitch
vectoangles (dir, angles);
if (angles[0] < -180)
angles[0] += 360;
if (fabs(angles[0]) > 45)
return;
trace_t tr; //!!
tr = gi.trace (start, NULL, NULL, self->s.origin, self, MASK_SHOT);
if (tr.ent != self) {
CTFResetGrapple(self);
return;
}
#endif
VectorCopy (self->s.origin, end);
gi.WriteByte (svc_temp_entity);
#if 1 //def USE_GRAPPLE_CABLE
gi.WriteByte (TE_GRAPPLE_CABLE);
gi.WriteShort (self->owner - g_edicts);
gi.WritePosition (self->owner->s.origin);
gi.WritePosition (end);
gi.WritePosition (offset);
#else
gi.WriteByte (TE_MEDIC_CABLE_ATTACK);
gi.WriteShort (self - g_edicts);
gi.WritePosition (end);
gi.WritePosition (start);
#endif
gi.multicast (self->s.origin, MULTICAST_PVS);
}
void CTFFireGrapple2 (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, int effect)
{
edict_t *grapple;
trace_t tr;
#ifdef USE_OLD_GRAPPLE
CTFFireGrapple (self, start, dir, damage, speed, effect);
return;
#endif
VectorNormalize (dir);
grapple = G_Spawn();
VectorCopy (start, grapple->s.origin);
VectorCopy (start, grapple->s.old_origin);
vectoangles (dir, grapple->s.angles);
VectorScale (dir, speed, grapple->velocity);
VectorSet(grapple->avelocity, 0, 0, 500);//tut
grapple->classname = "hook";//tut
grapple->movetype = MOVETYPE_FLYMISSILE;
grapple->clipmask = MASK_SHOT;
grapple->svflags |= SVF_DEADMONSTER;//tut
grapple->solid = SOLID_BBOX;
grapple->s.effects |= effect;
VectorClear (grapple->mins);
VectorClear (grapple->maxs);
grapple->s.modelindex = gi.modelindex ("models/weapons/grapple/hook/tris.md2");
grapple->owner = self;
grapple->touch = CTFGrappleTouch2;
grapple->delay = level.time + DELAY_TIME;//tut
// grapple->nextthink = level.time;//tut
grapple->nextthink = level.time + FRAMETIME;
grapple->think = CTFGrappleThink2;
grapple->prethink = CTFGrappleDrawCable2;
grapple->health = 100;//tut
grapple->svflags = SVF_MONSTER;//tut
grapple->dmg = damage;
self->client->ctf_grapple = grapple;
self->client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
gi.linkentity (grapple);
tr = gi.trace (self->s.origin, NULL, NULL, grapple->s.origin, grapple, MASK_SHOT);
if (tr.fraction < 1.0)
{
VectorMA (grapple->s.origin, -10, dir, grapple->s.origin);
grapple->touch (grapple, tr.ent, NULL, NULL);
}
}
void CTFGrappleFire2 (edict_t *ent, vec3_t g_offset, int damage, int effect)
{
vec3_t forward, right;
vec3_t start;
vec3_t offset;
float volume = 1.0;
//Feign 5/99
if (ent->client->pers.feign)
return;
#ifdef USE_OLD_GRAPPLE
CTFGrappleFire (ent, g_offset, damage, effect);
return;
#endif
if (ent->client->ctf_grapple) //tut s not needed?
{
return;
}//tut e
if (ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
return; // it's already out
AngleVectors (ent->client->v_angle, forward, right, NULL);
VectorScale (forward, -2, ent->client->kick_origin);
ent->client->kick_angles[0] = -1;
VectorSet(offset, 24, 8, ent->viewheight-8+2);
VectorAdd (offset, g_offset, offset);
P_ProjectSource (ent->client, ent->s.origin, offset, forward, right, start);
if (ent->client->silencer_shots)
volume = 0.2;
gi.sound (ent, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grfire.wav"), volume, ATTN_NORM, 0);
CTFFireGrapple2 (ent, start, forward, damage, CTF_GRAPPLE_SPEED, effect);
ent->client->hook_touch = NULL;//tut
#if 0
// send muzzle flash
gi.WriteByte (svc_muzzleflash);
gi.WriteShort (ent-g_edicts);
gi.WriteByte (MZ_BLASTER);
gi.multicast (ent->s.origin, MULTICAST_PVS);
#endif
PlayerNoise(ent, start, PNOISE_WEAPON);
//Eraser START: record this position, so we drop a grapple node here, rather than where the player is when they leave the ground
if (!ent->bot_client)
{ botDebugPrint("Grapple pull\n");
VectorCopy(ent->s.origin, ent->animate_org);
}
//ERASER END
}
void CTFWeapon_Grapple_Fire2 (edict_t *ent)
{
int damage;
damage = 10;
CTFGrappleFire2 (ent, vec3_origin, damage, 0);
// ent->client->ps.gunframe++;//COed by Gregg Reno
}
void CTFWeapon_Grapple2 (edict_t *ent)
{
static int pause_frames[] = {10, 18, 27, 0};
static int fire_frames[] = {6, 0};
int prevstate;
#ifdef USE_OLD_GRAPPLE
CTFWeapon_Grapple (ent);
return;
#endif
// if the the attack button is still down, stay in the firing frame
if ((ent->client->buttons & BUTTON_ATTACK) &&
ent->client->weaponstate == WEAPON_FIRING &&
ent->client->ctf_grapple)
{
botDebugPrint("firing\n");
ent->client->ps.gunframe = 9;
}
if (!(ent->client->buttons & BUTTON_ATTACK) && ent->client->ctf_grapple)
{ botDebugPrint("reset 3\n");
CTFResetGrapple2(ent->client->ctf_grapple);
if (ent->client->weaponstate == WEAPON_FIRING)
ent->client->weaponstate = WEAPON_READY;
}
if (ent->client->newweapon && ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY &&
ent->client->weaponstate == WEAPON_FIRING)
{ botDebugPrint("state\n");
// he wants to change weapons while grappled
ent->client->weaponstate = WEAPON_DROPPING;
ent->client->ps.gunframe = 32;
}
prevstate = ent->client->weaponstate;
Weapon_Generic (ent, 5, 9, 31, 36, pause_frames, fire_frames,CTFWeapon_Grapple_Fire2);
// if we just switched back to grapple, immediately go to fire frame
if (prevstate == WEAPON_ACTIVATING &&
ent->client->weaponstate == WEAPON_READY &&
ent->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
{
if (!(ent->client->buttons & BUTTON_ATTACK))
ent->client->ps.gunframe = 9;
else
ent->client->ps.gunframe = 5;
ent->client->weaponstate = WEAPON_FIRING;
}
}
// ent is player
void CTFPlayerResetGrapple2(edict_t *ent)
{
if (ent->client && ent->client->ctf_grapple)
// CTFResetGrapple(ent->client->ctf_grapple);
CTFResetGrapple2(ent->client->ctf_grapple);
}
// self is grapple, not player
void CTFResetGrapple2(edict_t *self)
{
float volume = 1.0;
// gclient_t *cl;
edict_t *owner = self->owner;//tut
gclient_t *client = self->owner->client;//tut replaces cl
edict_t *link = self->teamchain;//tut used for map ents push code
client->on_hook = false;//tut
client->hook_touch = NULL;//tut
botDebugPrint("SelfReset %s\n",self->classname);
#ifdef USE_OLD_GRAPPLE
CTFResetGrapple(self);
return;
#endif
// if (self->owner->client->ctf_grapple)
// {
if (client->ctf_grapple != NULL)//tut section
{
client->ctf_grapple = NULL;//replaced cl
VectorClear(client->oldvelocity);
self->think = NULL;
if (self->enemy)
{
self->enemy->mynoise2 = NULL;
}
//tut section end
if (self->owner->client->silencer_shots)
volume = 0.2;
if ((self->owner) && (self->owner->client))
{
gi.sound (self->owner, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grreset.wav"), volume, ATTN_NORM, 0);
// cl = self->owner->client;
// client->ctf_grapple = NULL;
client->ctf_grapplereleasetime = level.time;
client->ctf_grapplestate = CTF_GRAPPLE_STATE_FLY; // we're firing, not on hook
client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION; //sever codefix test
}//tut
G_FreeEdict(self);//tut
}//tut
}
void CTFGrapplePull2(edict_t *player)
{
vec3_t hookDir ,v;
vec_t vlen;
edict_t *self = player->client->ctf_grapple;
#ifdef USE_OLD_GRAPPLE
CTFGrapplePull(player);
return;
#endif
if (strcmp(player->client->pers.weapon->classname, "weapon_grapple") == 0 &&
!player->client->newweapon &&
player->client->weaponstate != WEAPON_FIRING &&
player->client->weaponstate != WEAPON_ACTIVATING)
{
CTFResetGrapple2(self);
return;
}
if (self->enemy)
{
if (self->enemy->solid == SOLID_NOT)
{
CTFResetGrapple2(self);
return;
}
if (self->enemy->solid == SOLID_BBOX)
{
VectorScale(self->enemy->size, 0.5, v);
VectorAdd(v, self->enemy->s.origin, v);
VectorAdd(v, self->enemy->mins, self->s.origin);
gi.linkentity (self);
} else
VectorCopy(self->enemy->velocity, self->velocity);
if (self->enemy->takedamage &&
!CheckTeamDamage (self->enemy, self->owner))
{
float volume = 1.0;
if (self->owner->client->silencer_shots)
volume = 0.2;
T_Damage (self->enemy, self, self->owner, self->velocity, self->s.origin, vec3_origin, 1, 1, 0, MOD_GRAPPLE);
gi.sound (self, CHAN_WEAPON, gi.soundindex("weapons/grapple/grhurt.wav"), volume, ATTN_NORM, 0);
}
// he died /ERASER ADDED || self->enemy->deadflag
if (!self->enemy || self->enemy->deadflag)
{
CTFResetGrapple2(self);
return;
}
}
VectorSubtract(self->s.origin, player->s.origin,hookDir);
vlen = VectorNormalize(hookDir);
//vector speeds
if (vlen < 64)
{
VectorScale(hookDir, 100,player->velocity);
}
else
{
VectorScale(hookDir, CTF_GRAPPLE_PULL_SPEED,player->velocity);//tut
}
VectorCopy(hookDir, player->movedir);//tut
if (player->client->ctf_grapplestate > CTF_GRAPPLE_STATE_FLY)
{
//sever codefix test
/* vec3_t forward, up;
AngleVectors (self->owner->client->v_angle, forward, NULL, up);
VectorCopy(self->owner->s.origin, v);
v[2] += self->owner->viewheight;
VectorSubtract (self->s.origin, v, hookDir);*/
//server codefix end
if (player->client->ctf_grapplestate == CTF_GRAPPLE_STATE_PULL &&
vlen < 64) {
float volume = 1.0;
if (player->client->silencer_shots)
volume = 0.2;
self->owner->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; //sever codefix test
gi.sound (player, CHAN_RELIABLE+CHAN_WEAPON, gi.soundindex("weapons/grapple/grhang.wav"), volume, ATTN_NORM, 0);
player->client->ctf_grapplestate = CTF_GRAPPLE_STATE_HANG;
}
}
/*To move the player off the ground just a bit
so he doesn't staystuck (version 3.17 bug)tut*/
if (player->velocity[2] > 0)
{
vec3_t traceTo;
trace_t trace;
// find the point immediately above the player's origin
VectorCopy(player->s.origin, traceTo);
traceTo[2] += 1;
// trace to it
trace = gi.trace(traceTo, player->mins, player->maxs, traceTo, player, MASK_PLAYERSOLID);
// if there isn't a solid immediately above the player
if (!trace.startsolid)
{
player->s.origin[2] += 1; // make sure player off ground
}
}
}