/***************************************************************** 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 } } }