Python "NeckStretcher" Grappler

 

 

philip
profile | email

posted 11-20-98 11:39 AM CT (US)
Title: Python "NeckStretcher" Grappler
Difficulty: Moderate
By: Philip (aka Maj.Bitch)
Email: peblair@gv.net
Date: 11-17-98
Note: Please give credit where credit is due.
======================================================

I wanted to see if I could put together a weapon which
when fired at the player out of a Super Shotgun, attaches
itself to the player upon a direct hit and then automatically
whips out a cable and wraps the cable around the player's
neck and then auto-fires a grapple hook into the ceiling/sky
pulling the unlucky player up in a neck-breaking jerking
motion and hangs the bastard suspended in the air by the
cable! Well, introducing, the newest weapon in your arsenal,
The Neck-Stretcher!

What is it?

Well, first the player has to be in possession of a SuperShotgun
(because the device to be fired requires a big kick to get itself
launched) and in possession of 10 shotgun shells. Doesn't have
to be their current weapon though... They just need to have both
in their inventory.

Okay, once you've gotten the necessary items, you hit your aliased
key and a grapple hook comes flying out (without cable attached
although I did leave the code to do so in the source...) at the
targeted individual. If you hit the sky, then the hook simply
disappears into never-never land. If you hit a solid surface then
the hook throws out a shower of sparks at impact (before disappearing)
and gives of some radius damage to anybody nearby. If you hit a
player, then a spurt of blood comes of the player (no damage yet)
as the hook imbeds itself into their body armor.. Immediately
thereafter, the imbedded hook does a really quick check to see
if there is enough ceiling/sky height above this player. If there
is not then the hook disappears (So, fire this at players out in
the open or in an area with high ceilings!)..

If there is enough height above the player to hang them, then the
embedded hook wraps its tailcable around the player's neck and
then launches itself straight up and embeds itself into the ceiling
or sky directly above the player.. Then, the hook (with the necessary
sounds and stuff) begins yanking the player up into the air by its
cable attached to the player's neck until they are in mid-air. Then,
the hook jerks the player up and down in mid-air trying to snap their
neck! The helpless player screams in agony as their neck becomes longer
and longer. There is no escape from this baby!!

After the player has died, the hook & cable release themselves from
their attached spots and the dead body of the hanged player falls to
the ground in a crumpled heap!! (Man, this shit is really making me
mentally ill!! Rather, make that -> MORE MENTALLY ILL!!)

Okay, let's get started!

===============================================================

We need to add another MOD_* obituary flag. So, inside of
your g_local.h add the MOD_HANGED flag as the next integer
in the numerical sequence as shown by example:

#define MOD_CLUSTER_BOMBS 24 // Cluster Airstrike
#define MOD_ZYLON_GAS 25 // Gas Grenade
#define MOD_RAILSTRIKE 26 // BFG Nuke Airstrike
#define MOD_BFG_NUKE 27 // Railgun Airstrike
#define MOD_DECOY 28 // Decoy Blast
#define MOD_LASERDRONE 29 // LaserDrone Weapon
#define MOD_HANGED 30 // NEW FLAG HERE

============================================================

Okay, let's make some obituaries for your gameplay. Find your
ClientObituary() function in your p_client.c file and add the
following:

---------- FIND THESE LINES HERE -------------

case MOD_TARGET_BLASTER:
message="got blasted";
break;
------------ ADD THESE LINES HERE ------------
case MOD_HANGED:
message="was strung up";
break;

Further down in this same function:

-------------- FIND THESE LINES -----------

case MOD_G_SPLASH:
if (IsFemale(victim))
message="tripped on her own grenade";
else
message="tripped on his own grenade";
break;

----------- ADD THESE LINES HERE -------------
case MOD_HANGED:
message="was hung";
break;

Still further down in this same function:

---------- FIND THESE LINES HERE -------------

case MOD_TELEFRAG:
message="tried to invade";
message2="'s personal space";
break;

--------- ADD THESE LINES HERE ---------------
case MOD_HANGED:
message="was hanged by";
message2="";
break;

RememberYou should feel free to change these client
obituaries to whatever you think is more appropriate for
your particular mod..

===============================================================

Open up your g_cmds.c file and go near the bottom of your
ClientCommand() function and add the following as shown
by example..

---------- IF-ELSE STATEMENTS --------
else if (Q_stricmp(cmd, "decoy") == 0 )
SP_Decoy(ent);
else if (Q_stricmp(cmd, "drone") == 0 )
Cmd_LaserDrone_f(ent);
else if (Q_stricmp(cmd, "teleport") == 0)
Cmd_Teleport_f(ent);
----------- ADD THESE LINES ------
else if (Q_stricmp(cmd, "hangman") == 0)
Cmd_HangMan_f(ent);
else if .....

This will launch off the initial grapple hook with
a single stroke of your aliased key (if you have
the necessary items in your inventory).

============================================================

Add this prototype declaration to the top of you g_cmds.c
file:

void Cmd_HangMan_f(edict_t *ent);

============================================================
============================================================
Lets open you g_weapons.c file and add these helper routines
and the source code somewhere near the top of the file.

NOTE I've used these helper routines in many of my previous
tutorials so you may already have them someplace in your source.
If you already do have them, then you don't need them anymore.
Your compiler should warn you that you've already got these
functions previously defined..

Okay, add the following text to the top of your g_weapons.c file.

------------------ START HERE ------------------------

//======================================================
// True if Ent is valid, has client, and edict_t inuse.
//======================================================
qboolean G_EntExists(edict_t *ent) {
return ((ent) && (ent->client) && (ent->inuse));
}

//======================================================
void G_Spawn_Sparks(int type, vec3_t start, vec3_t movdir, vec3_t origin ) {
gi.WriteByte(svc_temp_entity);
gi.WriteByte(type);
gi.WritePosition(start);
gi.WriteDir(movdir);
gi.multicast(origin, MULTICAST_PVS);
}

//======================================================
void G_Spawn_Trails(int type, vec3_t start, vec3_t endpos, vec3_t origin ) {
gi.WriteByte(svc_temp_entity);
gi.WriteByte(type);
gi.WritePosition(start);
gi.WritePosition(endpos);
gi.multicast(origin, MULTICAST_PVS);
}

--------------------- END HERE -------------------
===============================================================

Create a new file g_hangman.c and paste in all of the following
code from start to stop as indicated:

--------------------- START HERE ------------------

#include "g_local.h"

//==========================================================
//=============== GRAPPLE HANGMAN'S WEAPON ================
//==========================================================

#define HOOK_SMACK_SOUND gi.soundindex("misc/menu3.wav")
#define HOOK_MOTOR3_SOUND gi.soundindex("plats/pt1_strt.wav")
#define HOOK_RETRACT_SOUND gi.soundindex("items/respawn1.wav")
#define HOOK_LAUNCH_SOUND gi.soundindex("items/respawn1.wav")

//==========================================================
void G_StartVector(edict_t *ent, vec3_t start) {
vec3_t offset, forward, right;
vec3_t zvec={0,0,0};

AngleVectors(ent->owner->client->v_angle, forward, right, NULL);
VectorSet(offset, 8, 8, ent->owner->viewheight-8);
VectorAdd(offset, zvec, offset);
P_ProjectSource_Reverse(ent->owner->client, ent->owner->s.origin, offset, forward, right, start);
}

//=========================================================
void Release_Hook_Cable(edict_t *hook2) {
G_FreeEdict(hook2->activator); // Free Cable entity first.
G_FreeEdict(hook2); // Free Hook entity.
}

//===========================================================
// Every 1 second, jerk the cable and stretch out their neck!
//===========================================================
void Hang_The_Bastard(edict_t *hook2) {
vec3_t zvec={0,0,0};
vec3_t start={0,0,0};
vec3_t cablevec={0,0,0};
float cablelen;
vec3_t velpart={0,0,0};
float f1=0, f2, dprod;

// Has player died? Then exit..
if (hook2->goalentity->health <= 0) {
gi.sound(hook2->goalentity, CHAN_HOOK, HOOK_RETRACT_SOUND, 1, ATTN_IDLE, 0);
Release_Hook_Cable(hook2);
return; }

// Make cable sounds
gi.sound(hook2->owner, CHAN_HOOK, HOOK_MOTOR3_SOUND, 1, ATTN_IDLE, 0);

// Get cable start vector..
G_StartVector(hook2, start);
VectorSubtract(hook2->s.origin, start, cablevec);
cablelen = VectorLength(cablevec);

// Hang the bastard by the neck!
if (cablelen > 0) {
dprod=DotProduct(hook2->goalentity->velocity, cablevec)/DotProduct(cablevec, cablevec);
VectorScale(cablevec, dprod, velpart);
f2=cablelen;
if (DotProduct(hook2->goalentity->velocity, cablevec) < 0) {
if (cablelen > 25)
VectorSubtract(hook2->goalentity->velocity, velpart, hook2->goalentity->velocity);
f1=f2; }
else
if (VectorLength(velpart) < f2)
f1=f2-VectorLength(velpart);
} // end if

// Jerking motion to snap their neck!!
VectorNormalize(cablevec);
VectorMA(hook2->goalentity->velocity, f1, cablevec, hook2->goalentity->velocity);

hook2->enemy = hook2->goalentity;

// Damage this player in decrements of 10 units per cycle.
T_Damage(hook2->goalentity, hook2, hook2->owner, zvec, hook2->s.origin, NULL, 10, 0, 0, MOD_SPLASH);

hook2->nextthink = level.time + 1.0; // Jerk'em every 1 second!!
}

//======================================================
// Second Hook fired straight up! What did it hit?
//======================================================
void Hook2_Touch(edict_t *hook2, edict_t *other, cplane_t *plane, csurface_t *surf) {
vec3_t zvec=(0,0,0);
vec3_t start={0,0,0};
vec3_t cablevec={0,0,0};
float cablelen;

// Hit another player? Damage and exit.
if (other->takedamage) {
T_Damage(other, hook2, hook2->owner, zvec, hook2->s.origin, NULL, 100, 100, 0, MOD_HANGED);
Release_Hook_Cable(hook2);
return; }

//
// Otherwise, hit ceiling or sky..
//

// Stop hook's spinning and forward motions.
VectorClear(hook2->avelocity);
VectorClear(hook2->velocity);

// Figure out cable's length
G_StartVector(hook2, start);
VectorSubtract(hook2->s.origin, start, cablevec);
cablelen = VectorLength(cablevec);

// Not enough height to hang the player
if (cablelen < 300) {
Release_Hook_Cable(hook2);
return; }

// Hang'em!!
hook2->touch = NULL;
hook2->think = Hang_The_Bastard;
hook2->nextthink = level.time + 0.1;
}

//=============================================================
// Make continuous cable appear between start and end vectors.
//=============================================================
void Cable_Think(edict_t *cable) {
vec3_t zvec=(0,0,0);

G_Spawn_Trails(TE_BFG_LASER, cable->owner->goalentity->s.origin, cable->owner->s.origin, cable->owner->s.origin);

cable->nextthink = level.time + 0.1; // Must redraw every frame!!
}

//=============================================================
// In-Flight Think() so that cable appears after grapple fired!
//=============================================================
void Hook1_Think(edict_t *hook) {

if (1) return; // No Visible cable for first hook

G_Spawn_Trails(TE_BFG_LASER, hook->owner->s.origin, hook->s.origin, hook->s.origin);

hook->nextthink = level.time + 0.1; // Must redraw every frame!!
}

//================================================================
// Separate entity so that cable think can function independently
// of the hook think so cable gets continuous redraw while player
// hangs in mid-air..
//================================================================
edict_t *Spawn_Cable(edict_t *hook, edict_t *other) {
edict_t *cable=NULL;
vec3_t up;

// Find out which direction is up!
AngleVectors(other->client->v_angle, NULL, NULL, up);

// Spawn the Cable entity.
cable = G_Spawn();
cable->owner = hook;
cable->goalentity = other;
VectorCopy(up, cable->movedir);
VectorCopy(other->s.origin, cable->s.origin);
cable->takedamage=DAMAGE_NO;
cable->movetype = MOVETYPE_NONE;
cable->solid = SOLID_NOT;
VectorClear(cable->mins);
VectorClear(cable->maxs);
cable->think = Cable_Think;
cable->nextthink = level.time + 0.1;

return cable;
}

//==========================================================
// Second hook fired UP from targeted player's origin.
//==========================================================
void Spawn_Hook2(edict_t *owner, edict_t *other) {
edict_t *hook2=NULL;
edict_t *cable=NULL;
vec3_t up;

// Find out which direction is up! Stupid Question!!
AngleVectors(other->client->v_angle, NULL, NULL, up);

// Spawn the Hook entity.
hook2 = G_Spawn();
hook2->owner = owner; // Link to original owner..
hook2->goalentity = other; // Link to targeted player.
VectorCopy(up, hook2->movedir);
VectorSet(hook2->avelocity,0,0,-2000);
VectorScale(up, 1600, hook2->velocity);
VectorCopy(other->s.origin, hook2->s.origin);
hook2->s.origin[2] += other->viewheight; // from the neck!
hook2->takedamage=DAMAGE_NO;
hook2->s.effects = EF_ROCKET;
hook2->movetype = MOVETYPE_FLYMISSILE;
hook2->clipmask = MASK_SHOT;
hook2->solid = SOLID_BBOX;
VectorSet(hook2->mins,-5,-5,-5);
VectorSet(hook2->maxs, 5, 5, 5);
hook2->s.modelindex = gi.modelindex("models/weapons/grapple/hook/tris.md2");

hook2->touch = Hook2_Touch;
hook2->think = NULL; // No think function needed.
hook2->nextthink = 0;

gi.linkentity(hook2);

cable=Spawn_Cable(hook2, other);
hook2->activator = cable; // Link Cable to Hook.
gi.linkentity(cable);
}

//=========================================================
// Fired grapple hook hit something... What did it hit?
//=========================================================
void Hook1_Touch(edict_t *hook, edict_t *other, cplane_t *plane, csurface_t *surf) {

// Smacked into a surface so Sound, Sparks, Radius Damage.
if (!(surf && surf->flags & SURF_SKY)) {
gi.sound(hook, CHAN_VOICE, HOOK_SMACK_SOUND, 1, ATTN_IDLE, 0);
G_Spawn_Sparks(TE_SCREEN_SPARKS, hook->s.origin, plane->normal, hook->s.origin);
T_RadiusDamage(hook, hook->owner, 30, NULL, 200, MOD_SPLASH); }
else
// Hit a Player? so launch 2nd Hook Straight UP!
if ((G_EntExists(other)) && (other->takedamage)) {
gi.sound(hook, CHAN_VOICE, HOOK_SMACK_SOUND, 1, ATTN_IDLE, 0);
G_Spawn_Sparks(TE_BLOOD, hook->s.origin, plane->normal, other->s.origin);
Spawn_Hook2(hook->owner, other); }

G_FreeEdict(hook); // Free Up First Hook;
}

//==========================================================
// Fire the hook at something in crosshairs..
//==========================================================
void Shoot_Hook(edict_t *ent) {
edict_t *hook=NULL;
vec3_t forward;

// derive forward direction vector
AngleVectors(ent->client->v_angle, forward, NULL, NULL);

// spawn hook
hook = G_Spawn();
hook->owner = ent;
ent->goalentity = ent; // Tie back for Cable..
VectorCopy(ent->s.origin, hook->s.origin);
VectorCopy(forward, hook->movedir);
VectorScale(hook->movedir, 1600, hook->velocity);
hook->movetype = MOVETYPE_FLY;
hook->clipmask = MASK_SHOT;
hook->solid = SOLID_BBOX;
VectorSet(hook->mins,-5,-5,-5);
VectorSet(hook->maxs, 5, 5, 5);
hook->takedamage=DAMAGE_NO;
hook->s.modelindex = gi.modelindex("models/weapons/grapple/hook/tris.md2");
VectorSet(hook->avelocity,0,0,-2000);

hook->touch = Hook1_Touch; // Execute at impact.
hook->think = Hook1_Think; // Execute during flight.
hook->nextthink = level.time + 0.1;

gi.linkentity(hook);

// Play Hook launch sound..
gi.sound(hook->owner, CHAN_AUTO, HOOK_LAUNCH_SOUND, 1, ATTN_NORM, 0);
}

//========================================================
void Cmd_HangMan_f(edict_t *ent) {
int index;

// if ent has shells then...
index=ITEM_INDEX(FindItem("shells"));
if ((ent->client->pers.inventory[index] > 10)
&& (ent->client->pers.weapon==FindItem("super shotgun"))) {
ent->client->pers.inventory[index] -= 10;
Shoot_Hook(ent); }
else
gi.centerprintf(ent, "HangMan requires SShotgun + 10 shells\n");
}

--------------------- STOP HERE -------------------

===============================================================
Lastly..

Remember to bind a key in your Autoexec.cfg file as shown
by example below:

bind h "hangman"

That's it!! Now get out there and HANG THE BASTARDS!

Have Fun!!

philip