stvoy-sp-sdk/game/g_weapon.cpp

3211 lines
88 KiB
C++
Raw Normal View History

2002-11-22 00:00:00 +00:00
// g_weapon.c
// perform the server side effects of a weapon firing
#include "g_local.h"
#include "g_functions.h"
#include "anims.h"
#include "b_local.h"
#include "objectives.h"
#include "..\cgame\cg_text.h"
extern vmCvar_t cg_thirdPerson;
static vec3_t forward, vright, up;
static vec3_t muzzle;
#define BORG_ADAPT_NUM_HITS 10
extern int killPlayerTimer;
void FX_BorgShield( vec3_t position, vec3_t dir );
void FX_BorgHit( vec3_t origin, vec3_t dir );
extern void CGCam_Shake( float intensity, int duration );
extern void G_SetEnemy( gentity_t *self, gentity_t *enemy );
void drop_charge(gentity_t *ent, vec3_t start, vec3_t dir);
void ViewHeightFix( gentity_t *ent );
qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker );
extern void CG_ChangeWeapon( int num );
extern qboolean G_PointInBounds( vec3_t point, vec3_t mins, vec3_t maxs );
extern qboolean G_BoxInBounds( vec3_t point, vec3_t mins, vec3_t maxs, vec3_t boundsMins, vec3_t boundsMaxs );
void W_TraceSetStart( gentity_t *ent, vec3_t start, vec3_t mins, vec3_t maxs )
{//make sure our start point isn't on the other side of a wall
trace_t tr;
vec3_t entMins;
vec3_t entMaxs;
VectorAdd( ent->currentOrigin, ent->mins, entMins );
VectorAdd( ent->currentOrigin, ent->maxs, entMaxs );
if ( G_BoxInBounds( start, mins, maxs, entMins, entMaxs ) )
{
return;
}
if ( !ent->client )
{
return;
}
gi.trace( &tr, ent->client->renderInfo.eyePoint, mins, maxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP );
if ( tr.startsolid || tr.allsolid )
{
return;
}
if ( tr.fraction < 1.0f )
{
VectorCopy( tr.endpos, start );
}
else
{
//trace was cleah
}
}
//---------------------------------------------------------
qboolean W_CheckBorgAdapt( gentity_t *attacker, int weapon, gentity_t *traceEnt, vec3_t endpos )
//---------------------------------------------------------
{
if ( !traceEnt )
return qtrue;
if ( traceEnt->client && traceEnt->client->race == RACE_BORG && traceEnt->NPC && traceEnt->NPC->aiFlags&NPCAI_SHIELDS )
{
// Adapt info is stored under the players persistent client info
if ( g_entities[0].client )
{
int *borgAdaptHits = g_entities[0].client->ps.borgAdaptHits;
//FIXME: use damage accumulated rather than number of hits?
borgAdaptHits[weapon]++;
if(borgAdaptHits[weapon] >= BORG_ADAPT_NUM_HITS)
{
//They've adapted
if(borgAdaptHits[weapon] == BORG_ADAPT_NUM_HITS)
{
G_UseTargets2(attacker, attacker->owner, "adapted");
}
vec3_t dir;
VectorSubtract( attacker->currentOrigin, endpos, dir );
VectorNormalize( dir );
FX_BorgShield( endpos, dir );
traceEnt->s.powerups |= ( 1 << PW_BORG_SHIELD );
traceEnt->client->ps.powerups[PW_BORG_SHIELD] = level.time + 50;
//FIXME: Add this to cgs.media and play this on the cgame side
G_Sound(traceEnt, G_SoundIndex("sound/enemies/borg/borgshield.wav"));
//make them still react to absorbed shots, but no anim
GEntity_PainFunc( traceEnt, attacker, -1 );
return qfalse;
}
else
{
//Hit them
vec3_t backward;
VectorScale(forward, -1, backward);
FX_BorgHit( endpos , backward );
return qtrue;
}
}
}
return qtrue;
}
/*
----------------------------------------------
PLAYER WEAPONS
----------------------------------------------
----------------------------------------------
PHASER
----------------------------------------------
*/
#define PHASER_WEAK_DAMAGE 1
#define PHASER_DAMAGE 4
#define PHASER_ALT_DAMAGE 10
//---------------------------------------------------------
void WP_FirePhaser( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
trace_t tr;
vec3_t start, end;
gentity_t *traceEnt;
qboolean do_damage = qtrue, weak = qfalse;
// Make sure that the damage trace corresponds with the trace done for the actual effect or weird things could happen
if ( ent->s.number == 0 )
VectorCopy( ent->client->renderInfo.eyePoint, start );
else
VectorCopy( muzzle, start );
VectorMA( start, -8, forward, start );
VectorMA( start, weaponData[WP_PHASER].range, forward, end);
// Find out who we've hit
gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT);
traceEnt = &g_entities[ tr.entityNum ];
AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED );
AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED );
// Make sure to check if the Borg have adapted...
do_damage = W_CheckBorgAdapt( ent, WP_PHASER, traceEnt, tr.endpos );
if ( ent->client )
{
if ( ent->client->ps.ammo[AMMO_PHASER] < 1 )
weak = qtrue;
}
if ( traceEnt->takedamage )
{
if ( LogAccuracyHit( traceEnt, ent ) )
ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
if ( do_damage )
{
if ( weak )
{
// If we are in weak mode, only damage every now and then.
if ( (rand() & 1) )
G_Damage( traceEnt, ent, ent, forward, tr.endpos, PHASER_WEAK_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PHASER );
}
else
{
if ( alt_fire )
G_Damage( traceEnt, ent, ent, forward, tr.endpos, PHASER_ALT_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PHASER_ALT );
else
G_Damage( traceEnt, ent, ent, forward, tr.endpos, PHASER_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_PHASER );
}
}
}
}
/*
----------------------------------------------
COMPRESSION RIFLE
----------------------------------------------
*/
#define CRIFLE_DAMAGE 25
#define COMPRESSION_SPREAD 100
//---------------------------------------------------------
void WP_FireCompressionRifle ( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
gentity_t *tent;
gentity_t *tent2 = 0;
gentity_t *traceEnt;
int damage = alt_fire ? CRIFLE_DAMAGE * 6 : CRIFLE_DAMAGE; // do butt-loads of damage for alt
trace_t tr;
vec3_t start, end;
qboolean do_damage = qtrue;
qboolean render_impact = qtrue;
if ( ent->s.number == 0 && !cg_thirdPerson.integer )
// The trace start will originate at the eye so we can ensure that it hits the crosshair.
// This can cause small clipping errors, but it's probably not that big of a deal at this point.
VectorCopy( ent->client->renderInfo.eyePoint, start );
else
VectorCopy( muzzle, start );
VectorMA( start, -8, forward, start );
VectorMA( start, 8192, forward, end );
gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT );
// If the beam hits a skybox, etc. it would look foolish to add in an explosion
if ( tr.surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse;
}
traceEnt = &g_entities[ tr.entityNum ];
// Render a shot in any case.
if ( alt_fire )
tent = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE_ALT );
else
tent = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE );
// Only add in impact stuff when told to do so
if ( render_impact )
{
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
// Create a simple impact type mark
tent2 = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE_HIT );
tent->s.otherEntityNum = traceEnt->s.number;
do_damage = W_CheckBorgAdapt( ent, WP_COMPRESSION_RIFLE, traceEnt, tr.endpos );
if( LogAccuracyHit( traceEnt, ent ) )
{
ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
}
}
else
{
if ( alt_fire)
{
// Do a discreet miss
tent2 = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE_ALT_MISS );
AddSightEvent( ent, tr.endpos, 256, AEL_SUSPICIOUS );
}
else
{
// Create an explosion
tent2 = G_TempEntity( tr.endpos, EV_COMPRESSION_RIFLE_MISS );
// FIXME: Is this really an ok place to add this?
if ( ent->s.number == 0 )
{
//Add the event
AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED );
AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED );
}
}
}
}
// Stash origins, etc. so that the effect can have access to them.
VectorCopy( muzzle, tent->s.origin2 );
if ( render_impact )
{
VectorCopy( muzzle, tent2->s.origin2 );
tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal );
tent2->s.weapon = tent->s.weapon = ent->s.weapon;
}
if ( traceEnt->takedamage && do_damage )
{
// Do less damage when it's an NPC that is shooting this weapon, this is done so that they can still shoot a lot
// but not necessarily kill everything for you.
if ( ent->client->ps.clientNum != 0 )
{
damage *= 0.5;
}
if ( alt_fire )
{
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_SNIPER );
}
else
{
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_CRIFLE );
}
}
}
/*
----------------------------------------------
IMOD
----------------------------------------------
*/
#define IMOD_DAMAGE 24
#define IMOD_ALT_DAMAGE 60
//---------------------------------------------------------
void WP_FireIMOD ( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
trace_t tr;
vec3_t start, end, d_dir;
gentity_t *tent;
gentity_t *tent2 = 0;
gentity_t *traceEnt;
qboolean render_impact = qtrue;
if ( ent->s.number == 0 && !cg_thirdPerson.integer )
// The trace start will originate at the eye so we can ensure that it hits the crosshair.
// This can cause small clipping errors, but it's probably not that big of a deal at this point.
VectorCopy( ent->client->renderInfo.eyePoint, start );
else
VectorCopy( muzzle, start );
VectorMA( start, -8, forward, start );
VectorMA ( start, weaponData[WP_IMOD].range, forward, end);
gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT);
// If the beam hits a skybox, etc. it would look foolish to add in an explosion
if ( tr.surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse;
}
traceEnt = &g_entities[ tr.entityNum ];
if ( ent->s.number == 0 )
{
//Add the event
AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED );
AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED );
}
// Create the events that will add in the necessary effects
if ( alt_fire )
{
tent = G_TempEntity( tr.endpos, EV_IMOD_ALTFIRE );
if ( render_impact )
{
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
tent2 = G_TempEntity( tr.endpos, EV_IMOD_ALTFIRE_HIT );
tent->s.otherEntityNum = traceEnt->s.number;
if( LogAccuracyHit( traceEnt, ent ) )
{
ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
}
}
else
{
tent2 = G_TempEntity( tr.endpos, EV_IMOD_ALTFIRE_MISS );
}
}
}
else
{
tent = G_TempEntity( tr.endpos, EV_IMOD );
if ( render_impact )
{
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
tent2 = G_TempEntity( tr.endpos, EV_IMOD_HIT );
tent->s.otherEntityNum = traceEnt->s.number;
if( LogAccuracyHit( traceEnt, ent ) )
{
ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
}
}
else
{
tent2 = G_TempEntity( tr.endpos, EV_IMOD_MISS );
}
}
}
if ( traceEnt->takedamage)
{
//For knockback - send them up in air
VectorCopy(forward, d_dir);
if(d_dir[2] < 0.30f)
d_dir[2] = 0.30f;
VectorNormalize(d_dir);
if ( alt_fire )
G_Damage( traceEnt, ent, ent, d_dir, tr.endpos, IMOD_ALT_DAMAGE*Q_flrand(0.8f, 1.2f), DAMAGE_NO_HIT_LOC|DAMAGE_DEATH_KNOCKBACK|DAMAGE_EXTRA_KNOCKBACK, MOD_IMOD );
else
G_Damage( traceEnt, ent, ent, d_dir, tr.endpos, IMOD_DAMAGE*Q_flrand(0.8f, 1.2f), DAMAGE_NO_HIT_LOC|DAMAGE_DEATH_KNOCKBACK|DAMAGE_EXTRA_KNOCKBACK, MOD_IMOD );
}
// Stash origins, etc. so the effect can have access to them
VectorCopy( muzzle, tent->s.origin2 );
if ( render_impact )
{
VectorCopy( muzzle, tent2->s.origin2 );
tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal );
tent2->s.weapon = tent->s.weapon = ent->s.weapon;
}
}
/*
----------------------------------------------
SCAVENGER
----------------------------------------------
*/
#define SCAV_SPREAD 0.5
#define SCAV_VELOCITY 1500
#define SCAV_DAMAGE 10
#define SCAV_NPC_DAMAGE_EASY 3
#define SCAV_NPC_DAMAGE_NORMAL 7
#define SCAV_NPC_DAMAGE_HARD 9
#define SCAV_ALT_DAMAGE 60
#define SCAV_ALT_SPLASH_RAD 80
#define SCAV_ALT_SPLASH_DAM 60
#define SCAV_ALT_VELOCITY 900
//---------------------------------------------------------
void FireScavengerBullet( gentity_t *ent, vec3_t start, vec3_t dir, qboolean greyscale )
//---------------------------------------------------------
{
gentity_t *bolt;
int damage = SCAV_DAMAGE;
int velocity = SCAV_VELOCITY;
bolt = G_Spawn();
bolt->classname = "scav_proj";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
if ( greyscale )
{
bolt->s.weapon = WP_CHAOTICA_GUARD_GUN;
}
else
{
bolt->s.weapon = WP_SCAVENGER_RIFLE;
}
bolt->owner = ent;
// See if we need to do less damage, otherwise you'll be dead instantly if this weapon is used against you
if ( ent->client )
{
if ( ent->client->ps.clientNum != 0 )
{ // Flags effect as being lite version for NPC's
bolt->count = 1;
// Lowering velocity seemed to help the effect look a bit cooler from 3rd person, that way you see a volley
// of bullets racing at you...
velocity *= 0.5;
}
else
{ // Flags effect as being the full beefy version for the player
bolt->count = 0;
}
}
//Do the damages
if ( ent->s.number != 0 )
{
if ( g_spskill->integer == 0 )
damage = SCAV_NPC_DAMAGE_EASY;
else if ( g_spskill->integer == 1 )
damage = SCAV_NPC_DAMAGE_NORMAL;
else
damage = SCAV_NPC_DAMAGE_HARD;
}
else
{
damage = SCAV_DAMAGE;
}
bolt->damage = damage;
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->methodOfDeath = MOD_SCAVENGER;
bolt->clipmask = MASK_SHOT;
bolt->s.pos.trType = TR_LINEAR ;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( dir, velocity, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( start, bolt->currentOrigin);
}
// Alt-fire...
//---------------------------------------------------------
void FireScavengerGrenade( gentity_t *ent, vec3_t start, vec3_t dir )
//---------------------------------------------------------
{
gentity_t *grenade;
grenade = G_Spawn();
grenade->classname = "scav_grenade";
grenade->nextthink = level.time + 9000;
grenade->e_ThinkFunc = thinkF_G_FreeEntity;
grenade->s.eType = ET_MISSILE;
grenade->s.weapon = WP_SCAVENGER_RIFLE;
grenade->owner = ent;
grenade->damage = SCAV_ALT_DAMAGE;
grenade->dflags = 0;
grenade->splashDamage = SCAV_ALT_SPLASH_DAM;
grenade->splashRadius = SCAV_ALT_SPLASH_RAD;
grenade->methodOfDeath = MOD_GRENADE;
grenade->splashMethodOfDeath = MOD_GRENADE_SPLASH;
grenade->clipmask = MASK_SHOT;
grenade->alt_fire = qtrue;
// How 'bout we give this thing a size...
VectorSet( grenade->maxs, 1.0f, 1.0f, 1.0f );
VectorScale( grenade->maxs, -1, grenade->mins );
grenade->s.pos.trType = TR_GRAVITY;
grenade->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( start, grenade->s.pos.trBase );
VectorScale( dir, random() * 100 + SCAV_ALT_VELOCITY, grenade->s.pos.trDelta );
grenade->s.pos.trDelta[2] += 140;
SnapVector( grenade->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, grenade->currentOrigin);
VectorCopy (start, grenade->pos1);
}
//---------------------------------------------------------
void WP_FireScavenger( gentity_t *ent, qboolean alt_fire, qboolean greyscale )
//---------------------------------------------------------
{
vec3_t dir, angles, temp_ang, temp_org;
vec3_t start;
float offset;
VectorCopy( forward, dir );
VectorCopy( muzzle, start );
if ( alt_fire )
{
FireScavengerGrenade( ent, start, dir );
}
else
{
if ( ent->s.number == 0 )
{
// FIXME: Move in from the side muzzle. But, it might be much better to have it just come from the main barrel...
VectorMA( start, -20, forward, start );
VectorMA( start, 1, vright, start );
VectorMA( start, -4, up, start );
}
vectoangles( dir, angles );
//Scavengers use their aim values as well as the gun's inherent inaccuracy
if ( ent->client && ent->NPC && ent->client->playerTeam == TEAM_SCAVENGERS )
{
float xofs, yofs;
xofs = angles[0] + ( crandom() * (SCAV_SPREAD+(ent->NPC->stats.aim*0.5)) );
yofs = angles[1] + ( crandom() * (SCAV_SPREAD+(ent->NPC->stats.aim*0.5)) );
VectorSet( temp_ang, xofs, yofs, angles[2] );
}
else
{
VectorSet( temp_ang, angles[0] + (crandom() * SCAV_SPREAD), angles[1] + (crandom() * SCAV_SPREAD), angles[2] );
}
AngleVectors( temp_ang, dir, NULL, NULL );
// try to make the shot alternate between barrels
offset = Q_irand(0, 1) * 2 + 1;
VectorMA( start, offset, vright, temp_org );
VectorMA( temp_org, offset, up, temp_org );
//FIXME if temp_org does not have clear trace to inside the bbox, don't shoot!
FireScavengerBullet( ent, temp_org, dir, greyscale );
}
}
/*
----------------------------------------------
STASIS
----------------------------------------------
*/
#define STASIS_VELOCITY 1000 //650
#define STASIS_SPREAD 5.0 //1.8 // Keep the spread relatively small so that you can get multiple projectile impacts when a badie is close
#define STASIS_ALT_DAMAGE 40
#define STASIS_ALT_DAMAGE2 20
#define STASIS_ALT_RIGHT_OFS 0.10
#define STASIS_ALT_UP_OFS 0.02
#define MAXRANGE_STASIS 8192
//---------------------------------------------------------
void FireStasisMissile( gentity_t *ent, vec3_t origin, vec3_t dir, int size )
//---------------------------------------------------------
{
gentity_t *bolt;
bolt = G_Spawn();
bolt->classname = "stasis_projectile";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_STASIS;
bolt->owner = ent;
bolt->damage = size * 12;
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->methodOfDeath = MOD_STASIS;
//bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH;
bolt->clipmask = MASK_SHOT;
bolt->trigger_formation = qfalse; // don't draw tail on first frame
// There are going to be a couple of different sized projectiles, so store 'em here
bolt->count = size;
// How 'bout we give this thing a size...
VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f );
VectorScale( bolt->maxs, -size, bolt->mins );
VectorScale( bolt->maxs, size, bolt->maxs );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time;
VectorCopy( origin, bolt->s.pos.trBase );
VectorScale( dir, STASIS_VELOCITY + ( 50 * size ), bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (origin, bolt->currentOrigin);
// Used by trails
VectorCopy (origin, bolt->pos1 );
VectorCopy (origin, bolt->pos2 );
}
//---------------------------------------------------------
void WP_FireStasisMain( gentity_t *ent )
//---------------------------------------------------------
{
vec3_t dir, start;
vec3_t angles, temp;
VectorCopy( forward, dir );
VectorCopy( muzzle, start );
vectoangles( dir, angles );
FireStasisMissile( ent, start, dir, 4 );
VectorCopy( angles, temp );
temp[YAW] += STASIS_SPREAD;
AngleVectors( temp, temp, NULL, NULL );
FireStasisMissile( ent, start, temp, 2 );
VectorCopy( angles, temp );
temp[YAW] -= STASIS_SPREAD;
AngleVectors( temp, temp, NULL, NULL );
FireStasisMissile( ent, start, temp, 2 );
}
void DoSmallStasisBeam(gentity_t *ent, vec3_t start, vec3_t dir)
{
qboolean do_damage;
vec3_t end;
trace_t tr;
gentity_t *traceEnt;
VectorMA(start, MAXRANGE_STASIS, dir, end);
gi.trace(&tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT);
traceEnt = &g_entities[ tr.entityNum ];
do_damage = W_CheckBorgAdapt( ent, WP_STASIS, traceEnt, tr.endpos );
if ( traceEnt->takedamage && do_damage )
{
//For knockback - send them up in air
if ( dir[2] < 0.20f )
{
dir[2] = 0.20f;
}
VectorNormalize( dir );
G_Damage(traceEnt, ent, ent, dir, tr.endpos, STASIS_ALT_DAMAGE2, DAMAGE_NO_HIT_LOC, MOD_STASIS );
}
}
//---------------------------------------------------------
void WP_FireStasisAlt( gentity_t *ent )
//---------------------------------------------------------
{
trace_t tr;
vec3_t end, d_dir, d_right, d_up={0,0,1};
gentity_t *tent;
gentity_t *tent2;
gentity_t *traceEnt;
qboolean render_impact = qtrue;
qboolean do_damage = qtrue;
// Find the main impact point
VectorMA (muzzle, MAXRANGE_STASIS, forward, end);
gi.trace ( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
// Rendering things like explosions when hitting a sky box would look bad, but you still want to see the beam
if ( tr.surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse;
}
traceEnt = &g_entities[ tr.entityNum ];
// Why am I doing this when I've got a right and up already? Well, because this is how it is calc'ed on the client side.
CrossProduct(forward, d_up, d_right);
CrossProduct(d_right, forward, d_up); // Change the "fake up" (0,0,1) to a "real up" (perpendicular to the forward vector).
// Fire a shot up and to the right.
VectorMA(forward, STASIS_ALT_RIGHT_OFS, d_right, d_dir);
VectorMA(d_dir, STASIS_ALT_UP_OFS, d_up, d_dir);
DoSmallStasisBeam(ent, muzzle, d_dir);
// Fire a shot up and to the left.
VectorMA(forward, -STASIS_ALT_RIGHT_OFS, d_right, d_dir);
VectorMA(d_dir, STASIS_ALT_UP_OFS, d_up, d_dir);
DoSmallStasisBeam(ent, muzzle, d_dir);
// Fire a shot a bit down and to the right.
VectorMA(forward, 2.0*STASIS_ALT_RIGHT_OFS, d_right, d_dir);
VectorMA(d_dir, -0.5*STASIS_ALT_UP_OFS, d_up, d_dir);
DoSmallStasisBeam(ent, muzzle, d_dir);
// Fire a shot up and to the left.
VectorMA(forward, -2.0*STASIS_ALT_RIGHT_OFS, d_right, d_dir);
VectorMA(d_dir, -0.5*STASIS_ALT_UP_OFS, d_up, d_dir);
DoSmallStasisBeam(ent, muzzle, d_dir);
// Main beam
tent = G_TempEntity( tr.endpos, EV_STASIS );
// Only add in impact stuff when told to do so
if ( render_impact )
{
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
tent2 = G_TempEntity( tr.endpos, EV_STASIS_HIT );
tent->s.otherEntityNum = traceEnt->s.number;
do_damage = W_CheckBorgAdapt( ent, WP_STASIS, traceEnt, tr.endpos );
}
else
{
tent2 = G_TempEntity( tr.endpos, EV_STASIS_MISS );
if ( ent->s.number == 0 )
{
//Add the event
AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED );
AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED );
}
}
// Stash origins, etc. so that the effects can have access to them
VectorCopy( muzzle, tent2->s.origin2 );
tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal );
tent2->s.weapon = tent->s.weapon = ent->s.weapon;
}
if ( traceEnt->takedamage && do_damage )
{
//For knockback - send them up in air
VectorCopy( forward, d_dir );
if ( d_dir[2] < 0.30f )
{
d_dir[2] = 0.30f;
}
VectorNormalize( d_dir );
G_Damage( traceEnt, ent, ent, d_dir, tr.endpos, STASIS_ALT_DAMAGE, DAMAGE_NO_HIT_LOC, MOD_STASIS );
}
// Stash origins, etc. so that the effects can have access to them
VectorCopy( muzzle, tent->s.origin2 );
}
//---------------------------------------------------------
void WP_FireStasis( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
// This was moved out of the FireWeapon switch statement below to keep things more consistent
if ( alt_fire )
{
WP_FireStasisAlt( ent );
}
else
{
WP_FireStasisMain( ent );
}
}
/*
----------------------------------------------
GRENADE LAUNCHER
----------------------------------------------
*/
#define GRENADE_DAMAGE 100
#define GRENADE_SPLASH_RAD 128
#define GRENADE_SPLASH_DAM 90
#define GRENADE_VELOCITY 900
#define GRENADE_TIME 6000
#define GRENADE_ALT_DAMAGE 100
#define GRENADE_ALT_SPLASH_RAD 128
#define GRENADE_ALT_SPLASH_DAM 90
#define GRENADE_ALT_VELOCITY 1600
#define GRENADE_ALT_TIME 3000
#define SHRAPNEL_DAMAGE 30
#define SHRAPNEL_DISTANCE 4096
#define SHRAPNEL_BITS 6
#define SHRAPNEL_RANDOM 3
#define SHRAPNEL_SPREAD 0.75
//---------------------------------------------------------
void grenadeExplode( gentity_t *ent )
//---------------------------------------------------------
{
vec3_t normal = { 0, 0, 1 };
vec3_t pos;
gentity_t *tent;
VectorSet( pos, ent->currentOrigin[0], ent->currentOrigin[1], ent->currentOrigin[2] + 8 );
G_RadiusDamage( ent->currentOrigin, ent->owner, GRENADE_SPLASH_DAM, GRENADE_SPLASH_RAD,
NULL, MOD_GRENADE);
tent = G_TempEntity( pos, EV_GRENADE_EXPLODE );
VectorCopy( normal, tent->s.origin2 );
G_FreeEntity( ent );
}
//---------------------------------------------------------
void WP_FireShrapnel( gentity_t *ent, vec3_t dir )
//---------------------------------------------------------
{
vec3_t end, org;
trace_t tr;
gentity_t *tent;
VectorCopy( ent->s.pos.trBase, org );
VectorMA( org, SHRAPNEL_DISTANCE, dir, end );
gi.trace ( &tr, org, NULL, NULL, end, 0, MASK_SHOT );
tent = G_TempEntity( org, EV_GRENADE_SHRAPNEL );
VectorCopy( tr.endpos, tent->s.origin2 );
VectorCopy( tr.plane.normal, tent->pos1 );
}
//---------------------------------------------------------
void grenadeSpewShrapnel( gentity_t *ent )
//---------------------------------------------------------
{
int i, t, num_bits;
vec3_t norm;
gentity_t *tent;
num_bits = (int)( SHRAPNEL_BITS + random() * SHRAPNEL_RANDOM );
tent = G_TempEntity( ent->currentOrigin, EV_GRENADE_SHRAPNEL_EXPLODE );
VectorCopy( ent->pos1, tent->pos1 );
if ( ent->owner && ent->owner->s.number == 0 )
{
//Add the event
AddSoundEvent( ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED );
AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED );
}
G_RadiusDamage( ent->currentOrigin, ent->owner, GRENADE_ALT_SPLASH_DAM, GRENADE_ALT_SPLASH_RAD,
NULL, MOD_GRENADE);
for ( i = 0; i < num_bits; i++ )
{
VectorCopy( ent->pos1, norm );
for ( t = 0; t < 3; t++ )
{
norm[t] += crandom() * SHRAPNEL_SPREAD;
}
VectorNormalize( norm );
WP_FireShrapnel( ent, norm );
}
ent->nextthink = level.time + 100;
ent->e_ThinkFunc = thinkF_G_FreeEntity;
}
//---------------------------------------------------------
void WP_FireGrenade( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
gentity_t *bolt;
vec3_t dir, start;
VectorCopy( forward, dir );
VectorCopy( muzzle, start );
bolt = G_Spawn();
if ( alt_fire )
{
bolt->classname = "grenade_alt_projectile";
bolt->nextthink = level.time + GRENADE_ALT_TIME; // How long 'til she blows
bolt->e_ThinkFunc = thinkF_grenadeSpewShrapnel;
bolt->alt_fire = qtrue;
bolt->s.eFlags |= EF_MISSILE_STICK;
VectorScale( dir, GRENADE_ALT_VELOCITY, bolt->s.pos.trDelta );
bolt->s.pos.trType = TR_GRAVITY;
bolt->trigger_formation = qtrue;
bolt->damage = GRENADE_ALT_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = GRENADE_ALT_SPLASH_DAM;
bolt->splashRadius = GRENADE_ALT_SPLASH_RAD;
}
else
{
bolt->classname = "grenade_projectile";
bolt->nextthink = level.time + GRENADE_TIME; // How long 'til she blows
bolt->e_ThinkFunc = thinkF_grenadeExplode;
bolt->s.eFlags |= EF_BOUNCE_HALF;
VectorScale( dir, GRENADE_VELOCITY, bolt->s.pos.trDelta );
bolt->s.pos.trDelta[2] += 120;
bolt->s.pos.trType = TR_GRAVITY;
bolt->damage = GRENADE_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = GRENADE_SPLASH_DAM;
bolt->splashRadius = GRENADE_SPLASH_RAD;
// How 'bout we give this thing a size...
VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f );
VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f );
}
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_GRENADE_LAUNCHER;
bolt->owner = bolt->owner = ent;
bolt->methodOfDeath = MOD_GRENADE;
//bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH;
bolt->clipmask = MASK_SHOT;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
W_TraceSetStart( ent, start, bolt->mins, bolt->maxs );//make sure our start point isn't on the other side of a wall
VectorCopy( start, bolt->s.pos.trBase );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->currentOrigin);
VectorCopy( start, bolt->pos2 );
}
/*
----------------------------------------------
TETRION
----------------------------------------------
*/
#define TETRION_SPREAD 275
#define TETRION_DAMAGE 4
#define NUM_TETRION_SHOTS 3
#define TETRION_ALT_VELOCITY 1500
#define TETRION_ALT_DAMAGE 40
//---------------------------------------------------------
void FireTetrionBullet( gentity_t *ent, vec3_t start, vec3_t dir )
//---------------------------------------------------------
{
gentity_t *tent = 0;
gentity_t *tent2 = 0;
gentity_t *traceEnt, *hitEnt = NULL;
trace_t tr;
vec3_t end;
float r;
float u;
qboolean render_impact = qtrue;
qboolean do_damage = qtrue;
VectorCopy( muzzle, start );
W_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
// To simulate fast firing, we'll just generate a couple of shots at once
for ( int i = 0; i < NUM_TETRION_SHOTS; i ++ )
{
// Add some sloppiness to the shot so it's harder to hit when you are far away
r = crandom()*TETRION_SPREAD;
u = crandom()*TETRION_SPREAD;
VectorMA (start, 8192, forward, end);
VectorMA (end, r, vright, end);
VectorMA (end, u, up, end);
gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT);
// Rendering things like impacts when hitting a sky box would look bad, but you still want to see the tracer
if ( tr.surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse;
}
traceEnt = &g_entities[ tr.entityNum ];
// Don't always draw a tracer line
if ( random() > 0.1 )
{
// Create the trace effect
tent = G_TempEntity( tr.endpos, EV_TETRION );
// Stash origins, etc. so that effects can have access to startPoints and other useful info.
VectorCopy( start, tent->s.origin2 );
tent->s.eventParm = DirToByte( tr.plane.normal );
tent->s.weapon = ent->s.weapon;
VectorScale( forward, -1, tent->pos1 ); // Used for trace-back
tent->s.otherEntityNum = traceEnt->s.number;
}
// Make sure we hit something that should generate an impact
if ( render_impact )
{
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
tent2 = G_TempEntity( tr.endpos, EV_TETRION_HIT );
do_damage = W_CheckBorgAdapt( ent, WP_TETRION_DISRUPTOR, traceEnt, tr.endpos );
if( LogAccuracyHit( traceEnt, ent ) )
{
hitEnt = traceEnt;
}
}
else
{
tent2 = G_TempEntity( tr.endpos, EV_TETRION_MISS );
if ( ent->s.number == 0 )
{
//Add the event
AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED );
AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED );
}
}
// Stash origins, etc. so that effects can have access to startPoints and other useful info.
VectorCopy( start, tent2->s.origin2 );
tent2->s.eventParm = DirToByte( tr.plane.normal );
tent2->s.weapon = ent->s.weapon;
}
// Let's do some damage to whomever or whatever we hit
if ( traceEnt->takedamage && do_damage )
{
G_Damage( traceEnt, ent, ent, forward, tr.endpos, TETRION_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_TETRION );
}
} // next shot trace
if ( hitEnt )
{
ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
}
}
//---------------------------------------------------------
void FireTetrionProjectile( gentity_t *ent, vec3_t start, vec3_t dir )
//---------------------------------------------------------
{// Projectile that bounces off surfaces but does not have gravity
gentity_t *bolt;
bolt = G_Spawn();
bolt->classname = "tetrion_projectile";
bolt->nextthink = level.time + 4000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.eFlags |= EF_BOUNCE;
bolt->bounceCount = Q_irand( 3, 5 ); // Give 'em three to five bounces
bolt->alt_fire = qtrue;
bolt->s.weapon = WP_TETRION_DISRUPTOR;
bolt->damage = TETRION_ALT_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->owner = ent;
bolt->methodOfDeath = MOD_TETRION;
//bolt->splashMethodOfDeath = MOD_ROCKET_SPLASH;
bolt->clipmask = MASK_SHOT;
// How 'bout we give this thing a size...
VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f );
VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
W_TraceSetStart( ent, start, bolt->mins, bolt->maxs );//make sure our start point isn't on the other side of a wall
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( dir, TETRION_ALT_VELOCITY, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->currentOrigin);
}
//---------------------------------------------------------
void WP_FireTetrionDisruptor( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
vec3_t dir;
vec3_t start;
VectorCopy( forward, dir );
VectorCopy( muzzle, start );
VectorNormalize (dir);
if ( alt_fire )
{
FireTetrionProjectile( ent, start, dir );
}
else
{
FireTetrionBullet( ent, start, dir );
}
}
/*
----------------------------------------------
DREADNOUGHT
----------------------------------------------
*/
#define DREADNOUGHT_DAMAGE 13
#define DN_VELOCITY 1400
#define DREADNOUGHT_ALT_DAMAGE 60
void WP_DreadnoughtBurstThink( gentity_t *ent );
//---------------------------------------------------------
void WP_FireDreadnoughtBeam( gentity_t *ent )
//---------------------------------------------------------
{
trace_t tr;
vec3_t start, end, mins, maxs;
gentity_t *traceEnt;
qboolean do_damage = qtrue;
// Make sure that the damage trace corresponds with the trace done for the actual effect or weird things could happen
if ( ent->s.number == 0 )
VectorCopy( ent->client->renderInfo.eyePoint, start );
else
VectorCopy( muzzle, start );
VectorMA( start, weaponData[WP_DREADNOUGHT].range, forward, end );
// Give the beam a bit of meat
VectorSet( maxs, 2.0f, 2.0f, 2.0f );
VectorScale( maxs, -1.0f, mins );
// Find out who we've hit
gi.trace( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
traceEnt = &g_entities[ tr.entityNum ];
if ( !traceEnt->takedamage && ent->s.number == 0 )
{
//Add the event
AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED );
AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED );
}
do_damage = W_CheckBorgAdapt( ent, WP_DREADNOUGHT, traceEnt, tr.endpos );
if ( traceEnt->takedamage )
{
if ( LogAccuracyHit( traceEnt, ent ) )
{
ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
}
if ( do_damage )
{
G_Damage( traceEnt, ent, ent, forward, tr.endpos, DREADNOUGHT_DAMAGE, 0, MOD_DREADNOUGHT);
}
}
}
#define DN_SEARCH_DIST 128
#define DN_SIDE_DIST 128
#define DN_RAND_DEV 24
#define DN_ALT_THINK_TIME 100
#define DN_ALT_SIZE 12
//---------------------------------------------------------
void WP_FireDreadnoughtBurst( gentity_t *ent )
//---------------------------------------------------------
{
gentity_t *bolt;
vec3_t dir, start;
VectorCopy( forward, dir );
VectorCopy( muzzle, start );
bolt = G_Spawn();
bolt->classname = "dn_projectile";
bolt->nextthink = level.time + 200;
bolt->e_ThinkFunc = thinkF_WP_DreadnoughtBurstThink;
bolt->count = 5; //how many times it can damage something
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_DREADNOUGHT;
bolt->owner = ent;
bolt->damage = DREADNOUGHT_ALT_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->methodOfDeath = MOD_DREADNOUGHT;
bolt->clipmask = MASK_SHOT;
bolt->alt_fire = qtrue;
bolt->fx_time = level.time;
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time;
{
vec3_t mins={-DN_ALT_SIZE,-DN_ALT_SIZE,-1}, maxs={DN_ALT_SIZE,DN_ALT_SIZE,1};
W_TraceSetStart( ent, start, mins, maxs );//make sure our start point isn't on the other side of a wall
}
VectorCopy( start, bolt->s.pos.trBase );
VectorCopy( start, bolt->s.origin);
//VectorScale( dir, DN_VELOCITY, bolt->s.pos.trDelta );
VectorCopy( forward, bolt->movedir);
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( start, bolt->currentOrigin );
VectorCopy( start, bolt->pos1 );
VectorCopy( start, bolt->pos2 );
WP_DreadnoughtBurstThink(bolt);
}
//---------------------------------------------------------
void WP_DreadnoughtBurstThink( gentity_t *ent )
//---------------------------------------------------------
{
static qboolean bAlreadyHere = qfalse;
vec3_t startpos, endpos, perp;
trace_t tr;
gentity_t *traceEnt, *tent;
int source;
vec3_t dest, mins={-DN_ALT_SIZE,-DN_ALT_SIZE,-1}, maxs={DN_ALT_SIZE,DN_ALT_SIZE,1};
float dot;
ent->count--; //dec the count of repeat hits
VectorCopy(ent->s.origin, startpos);
// Search in a 3-way arc in front of it.
VectorMA( startpos, DN_SEARCH_DIST, ent->movedir, endpos );
endpos[0] += crandom() * DN_RAND_DEV;
endpos[1] += crandom() * DN_RAND_DEV;
endpos[2] += crandom() * DN_RAND_DEV * 0.5f;
// unlink this entity, so the next trace will go past it
gi.unlinkentity( ent );
if ( ent->enemy ) {
source = ent->enemy->s.number;
} else if ( ent->owner ) {
source = ent->owner->s.number;
} else {
source = ent->s.number;
}
gi.trace( &tr, startpos, mins, maxs, endpos, source, MASK_SHOT );
traceEnt = &g_entities[ tr.entityNum ];
if ( traceEnt->takedamage && traceEnt->health > 0 && ent->count > 0 )
{
if ( traceEnt->client )
{
if ( traceEnt->client->playerTeam == TEAM_STARFLEET || traceEnt->client->noclip )
{
// Scale down damage, hurt them, but don't stick on them
G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage * 0.5f, 0, MOD_DREADNOUGHT );
goto ignore1;
}
G_SetEnemy(ent, traceEnt);
VectorCopy( ent->s.origin, ent->s.origin2 );
VectorCopy( traceEnt->currentOrigin, ent->s.origin );
gi.linkentity( ent );
VectorNormalize( ent->movedir );
ent->nextthink = level.time + DN_ALT_THINK_TIME;
G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT );
return;
}
G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT );
}
else
{
if ( tr.fraction < 0.02f || ent->count < 1 )
{
// Hit a wall...
dot = DotProduct( ent->movedir, tr.plane.normal );
if ( !traceEnt->takedamage && ent->owner && ent->owner->s.number == 0 )
{
//Add the event
AddSoundEvent( ent->owner, tr.endpos, 256, AEL_DISCOVERED );
AddSightEvent( ent->owner, tr.endpos, 512, AEL_DISCOVERED );
}
if ( dot < -0.6f || dot == 0.0f || (tr.surfaceFlags & SURF_NOIMPACT) || ent->count < 1 )
{
// Stop.
G_FreeEntity( ent );
if ( !(tr.surfaceFlags & SURF_NOIMPACT) )
{
tent = G_TempEntity( tr.endpos, EV_DREADNOUGHT_MISS );
// Stash origins, etc. so that the effects can have access to them
VectorCopy( startpos, tent->s.origin2 );
tent->s.eventParm = DirToByte( tr.plane.normal );
}
return;
}
else
{
// Bounce off the surface just a little
VectorMA( ent->movedir, -1.25f * dot, tr.plane.normal, ent->movedir );
VectorNormalize( ent->movedir );
// Make sure we store a next think time, else the effect could sit around forever...
ent->nextthink = level.time + DN_ALT_THINK_TIME;
// this stops a runaway crash bug when the insects break through the corridor in voy16
if (!bAlreadyHere)
{
bAlreadyHere = qtrue;
WP_DreadnoughtBurstThink( ent );// NOTE RECURSION HERE.
bAlreadyHere = qfalse;
}
return;
}
}
VectorCopy( tr.endpos, dest );
}
// Didn't hit anything forward. Try some side vectors.
perp[0] = ent->movedir[1];
perp[1] = -ent->movedir[0];
perp[2] = ent->movedir[2];
// Search a random interval from the side arc
VectorMA( endpos, DN_SIDE_DIST, perp, endpos );
endpos[0] += crandom() * DN_RAND_DEV;
endpos[1] += crandom() * DN_RAND_DEV;
endpos[2] += crandom() * DN_RAND_DEV * 0.5f;
gi.trace( &tr, startpos, mins, maxs, endpos, source, MASK_SHOT );
traceEnt = &g_entities[ tr.entityNum ];
if ( traceEnt->takedamage && traceEnt->health > 0 )
{
if ( traceEnt->client )
{
if ( traceEnt->client->playerTeam == TEAM_STARFLEET || traceEnt->client->noclip )
{
goto ignore1;
}
G_SetEnemy( ent, traceEnt );
VectorCopy( ent->s.origin, ent->s.origin2 );
VectorCopy( traceEnt->currentOrigin, ent->s.origin );
gi.linkentity( ent );
VectorNormalize( ent->movedir );
ent->nextthink = level.time + DN_ALT_THINK_TIME;
G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT );
return;
}
G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT );
}
// Search a random interval in the opposite direction
VectorMA( endpos, -2.0f * DN_SIDE_DIST, perp, endpos );
endpos[0] += crandom() * DN_RAND_DEV;
endpos[1] += crandom() * DN_RAND_DEV;
endpos[2] += crandom() * DN_RAND_DEV * 0.5f;
gi.trace( &tr, startpos, mins, maxs, endpos, source, MASK_SHOT );
traceEnt = &g_entities[ tr.entityNum ];
if ( traceEnt->takedamage && traceEnt->health > 0 )
{
if ( traceEnt->client )
{
if ( traceEnt->client->playerTeam == TEAM_STARFLEET || traceEnt->client->noclip )
{
goto ignore1;
}
G_SetEnemy(ent, traceEnt);
VectorCopy( ent->s.origin, ent->s.origin2 );
VectorCopy( traceEnt->currentOrigin, ent->s.origin );
gi.linkentity( ent );
VectorNormalize( ent->movedir );
ent->nextthink = level.time + DN_ALT_THINK_TIME;
G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT );
return;
}
G_Damage( traceEnt, ent, ent->owner, forward, tr.endpos, ent->damage, 0, MOD_DREADNOUGHT );
}
ignore1:
// We didn't find anything, so move the entity to the middle destination.
ent->enemy = NULL;
VectorCopy( ent->s.origin, ent->s.origin2 );
VectorCopy( dest, ent->s.origin );
gi.linkentity( ent );
ent->nextthink = level.time + DN_ALT_THINK_TIME;
return;
}
//---------------------------------------------------------
void WP_FireDreadnought( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
// This was moved out of the FireWeapon switch statement below to keep things more consistent
if ( alt_fire )
{
WP_FireDreadnoughtBurst( ent );
}
else
{
WP_FireDreadnoughtBeam( ent );
}
}
/*
----------------------------------------------
QUANTUM BURST
----------------------------------------------
*/
#define QUANTUM_VELOCITY 900
#define QUANTUM_DAMAGE 100
#define QUANTUM_SPLASH_DAM 64
#define QUANTUM_SPLASH_RAD 128
#define QUANTUM_ALT_VELOCITY 500
#define QUANTUM_ALT_DAMAGE 125
#define QUANTUM_ALT_SPLASH_DAM 100
#define QUANTUM_ALT_SPLASH_RAD 128
#define QUANTUM_ALT_THINK_TIME 200
#define QUANTUM_ALT_SEARCH_DIST 1024
//---------------------------------------------------------
void FireQuantumBurst( gentity_t *ent, vec3_t start, vec3_t dir )
//---------------------------------------------------------
{
gentity_t *bolt;
bolt = G_Spawn();
bolt->classname = "quantum_projectile";
bolt->nextthink = level.time + 6000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_QUANTUM_BURST;
bolt->owner = ent;
bolt->damage = QUANTUM_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = QUANTUM_SPLASH_DAM;
bolt->splashRadius = QUANTUM_SPLASH_RAD;
bolt->methodOfDeath = MOD_QUANTUM;
bolt->splashMethodOfDeath = MOD_QUANTUM_SPLASH;
bolt->clipmask = MASK_SHOT;
// How 'bout we give this thing a size...
VectorSet( bolt->mins, -3.0f, -3.0f, -3.0f );
VectorSet( bolt->maxs, 3.0f, 3.0f, 3.0f );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( dir, QUANTUM_VELOCITY, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->currentOrigin);
VectorCopy (start, bolt->pos1);
}
//---------------------------------------------------------
void FireQuantumBurstAlt( gentity_t *ent, vec3_t start, vec3_t dir )
//---------------------------------------------------------
{
gentity_t *bolt;
bolt = G_Spawn();
bolt->classname = "quantum_alt_projectile";
// bolt->nextthink = level.time + 6000;
// bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->nextthink = level.time + 100;
bolt->e_ThinkFunc = thinkF_WP_QuantumAltThink;
bolt->health = 50; // 10 seconds.
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_QUANTUM_BURST;
bolt->owner = ent;
bolt->alt_fire = qtrue; // Things won't work if this doesn't get set!!
bolt->damage = QUANTUM_ALT_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = QUANTUM_ALT_SPLASH_DAM;
bolt->splashRadius = QUANTUM_ALT_SPLASH_RAD;
bolt->methodOfDeath = MOD_QUANTUM;
bolt->splashMethodOfDeath = MOD_QUANTUM_SPLASH;
bolt->clipmask = MASK_SHOT;
// How 'bout we give this thing a size...
VectorSet( bolt->mins, -3.0f, -3.0f, -3.0f );
VectorSet( bolt->maxs, 3.0f, 3.0f, 3.0f );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( dir, QUANTUM_ALT_VELOCITY, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->currentOrigin);
VectorCopy(dir, bolt->movedir);
}
//---------------------------------------------------------
qboolean SearchTarget( gentity_t *ent, vec3_t start, vec3_t end )
{
trace_t tr;
gentity_t *traceEnt;
vec3_t fwd;
gi.trace( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT );
traceEnt = &g_entities[ tr.entityNum ];
if( traceEnt->takedamage && traceEnt->client && traceEnt->health > 0 )
{
G_SetEnemy(ent, traceEnt);
if ( ent->enemy )
{
VectorSubtract( ent->enemy->currentOrigin, ent->currentOrigin, fwd );
VectorNormalize( fwd );
VectorScale( fwd, QUANTUM_ALT_VELOCITY, ent->s.pos.trDelta );
VectorCopy( fwd, ent->movedir );
SnapVector( ent->s.pos.trDelta ); // save net bandwidth
VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
ent->s.pos.trTime = level.time;
ent->nextthink = level.time + QUANTUM_ALT_THINK_TIME;
return qtrue;
}
}
return qfalse;
}
//---------------------------------------------------------
void WP_QuantumAltThink( gentity_t *ent )
{
vec3_t start, newdir, targetdir, up={0,0,1}, right, search;
float dot, dot2;
ent->health--;
if( ent->health <= 0 )
{
G_FreeEntity( ent );
return;
}
if ( ent->enemy )
{
// Already have a target, start homing.
if ( !ent->enemy->inuse || ent->enemy->health <= 0 )
{
// No longer target this.
ent->enemy = NULL;
ent->nextthink = level.time + 1000;
ent->health -= 5;
return;
}
VectorSubtract( ent->enemy->currentOrigin, ent->currentOrigin, targetdir );
VectorNormalize( targetdir );
// Now the rocket can't do a 180 in space, so we'll limit the turn to about 45 degrees.
dot = DotProduct( targetdir, ent->movedir );
// a dot of 1.0 means right-on-target.
if ( dot < 0.0f )
{
// Go in the direction opposite, start a 180.
CrossProduct( ent->movedir, up, right );
dot2 = DotProduct( targetdir, right );
if ( dot2 > 0 )
{
// Turn 45 degrees right.
VectorAdd( ent->movedir, right, newdir );
}
else
{
// Turn 45 degrees left.
VectorSubtract(ent->movedir, right, newdir);
}
// Yeah we've adjusted horizontally, but let's split the difference vertically, so we kinda try to move towards it.
newdir[2] = ( targetdir[2] + ent->movedir[2] ) * 0.5;
VectorNormalize( newdir );
}
else if ( dot < 0.7 )
{
// Need about one correcting turn. Generate by meeting the target direction "halfway".
// Note, this is less than a 45 degree turn, but it is sufficient. We do this because the rocket may have to go UP.
VectorAdd( ent->movedir, targetdir, newdir );
VectorNormalize( newdir );
}
else
{
// else adjust to right on target.
VectorCopy( targetdir, newdir );
}
VectorScale( newdir, QUANTUM_ALT_VELOCITY, ent->s.pos.trDelta );
VectorCopy( newdir, ent->movedir );
SnapVector( ent->s.pos.trDelta ); // save net bandwidth
VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
ent->s.pos.trTime = level.time;
}
else
{
// Search in front of the missile for targets.
VectorCopy( ent->currentOrigin, start );
CrossProduct( ent->movedir, up, right );
// Search straight ahead.
VectorMA( start, QUANTUM_ALT_SEARCH_DIST, ent->movedir, search );
if ( SearchTarget( ent, start, search ))
return;
// Search to the right.
VectorMA( search, QUANTUM_ALT_SEARCH_DIST * 0.1f, right, search );
if ( SearchTarget(ent, start, search ))
return;
// Search to the left.
VectorMA( search, -QUANTUM_ALT_SEARCH_DIST * 0.2f, right, search );
if ( SearchTarget( ent, start, search ))
return;
}
ent->nextthink = level.time + QUANTUM_ALT_THINK_TIME; // Nothing at all spectacular happened, continue.
return;
}
//---------------------------------------------------------
void WP_FireQuantumBurst( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
vec3_t dir, start;
VectorCopy( forward, dir );
VectorCopy( muzzle, start );
if ( alt_fire )
{
FireQuantumBurstAlt( ent, start, dir );
}
else
{
FireQuantumBurst( ent, start, dir );
}
}
//---------------------------------------------------------
void WP_TricorderScan (gentity_t *ent, qboolean alt_fire)
//---------------------------------------------------------
{
static int sound_debounce_time;
if ( sound_debounce_time < level.time )
{
if ( alt_fire )
{
sound_debounce_time = level.time + Q_irand(500, 1500);
}
else
{
sound_debounce_time = level.time + 5000;
}
}
}
/*
----------------------------------------------
PROTON GUN
----------------------------------------------
*/
#define PROTON_DAMAGE 25
#define COMPRESSION_SPREAD 100
//---------------------------------------------------------
void WP_FireProtonGun ( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
gentity_t *tent;
gentity_t *tent2 = 0;
gentity_t *traceEnt;
int damage = alt_fire ? PROTON_DAMAGE * 6 : PROTON_DAMAGE; // do butt-loads of damage for alt
trace_t tr;
vec3_t start, end;
qboolean do_damage = qtrue;
qboolean render_impact = qtrue;
if ( ent->s.number == 0 && !cg_thirdPerson.integer )
// The trace start will originate at the eye so we can ensure that it hits the crosshair.
// This can cause small clipping errors, but it's probably not that big of a deal at this point.
VectorCopy( ent->client->renderInfo.eyePoint, start );
else
VectorCopy( muzzle, start );
VectorMA( start, -8, forward, start );
VectorMA( start, 8192, forward, end );
gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT );
// If the beam hits a skybox, etc. it would look foolish to add in an explosion
if ( tr.surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse;
}
traceEnt = &g_entities[ tr.entityNum ];
// Render a shot in any case.
if ( alt_fire )
tent = G_TempEntity( tr.endpos, EV_PROTON_GUN_ALT );
else
tent = G_TempEntity( tr.endpos, EV_PROTON_GUN );
// Only add in impact stuff when told to do so
if ( render_impact )
{
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
// Create a simple impact type mark
tent2 = G_TempEntity( tr.endpos, EV_PROTON_GUN_HIT );
tent->s.otherEntityNum = traceEnt->s.number;
do_damage = W_CheckBorgAdapt( ent, WP_PROTON_GUN, traceEnt, tr.endpos );
if( LogAccuracyHit( traceEnt, ent ) )
{
ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
}
}
else
{
if ( alt_fire)
{
// Do a discreet miss
tent2 = G_TempEntity( tr.endpos, EV_PROTON_GUN_ALT_MISS );
AddSightEvent( ent, tr.endpos, 256, AEL_SUSPICIOUS );
}
else
{
// Create an explosion
tent2 = G_TempEntity( tr.endpos, EV_PROTON_GUN_MISS );
// FIXME: Is this really an ok place to add this?
if ( ent->s.number == 0 )
{
//Add the event
AddSoundEvent( ent, tr.endpos, 256, AEL_DISCOVERED );
AddSightEvent( ent, tr.endpos, 512, AEL_DISCOVERED );
}
}
}
}
// Stash origins, etc. so that the effect can have access to them.
VectorCopy( muzzle, tent->s.origin2 );
if ( render_impact )
{
VectorCopy( muzzle, tent2->s.origin2 );
tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal );
tent2->s.weapon = tent->s.weapon = ent->s.weapon;
}
if ( traceEnt->takedamage && do_damage )
{
// Do less damage when it's an NPC that is shooting this weapon, this is done so that they can still shoot a lot
// but not necessarily kill everything for you.
if ( ent->client->ps.clientNum != 0 )
{
damage *= 0.5;
}
if ( alt_fire )
{
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_SNIPER );
}
else
{
G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_CRIFLE );
}
}
}
/*
----------------------------------------------
BORG WEAPONS
----------------------------------------------
*/
#define BORG_PROJ_DAMAGE 10
#define BORG_PROJ_SPEED 500
//---------------------------------------------------------
void WP_FireBorgWeapon( gentity_t *ent )
//---------------------------------------------------------
{
gentity_t *bolt;
vec3_t dir, start;
VectorCopy( forward, dir );
VectorCopy( muzzle, start );
VectorNormalize (dir);
bolt = G_Spawn();
bolt->classname = "borg_projectile";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_BORG_WEAPON;
bolt->owner = ent;
bolt->damage = BORG_PROJ_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->methodOfDeath = MOD_ENERGY;
bolt->clipmask = MASK_SHOT;
// How 'bout we give this thing a size...
VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f );
VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( dir, BORG_PROJ_SPEED, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->currentOrigin);
}
#define BORG_ATTACK_DELAY 100
void CG_DrawNode( vec3_t origin, int type );
//---------------------------------------------------------
void WP_BorgAssimilate(gentity_t *borg)
//---------------------------------------------------------
{
trace_t tr;
vec3_t end;
gentity_t *traceEnt;
if ( borg->attackDebounceTime > level.time )
return;
muzzle[2] -= 8;
VectorMA (muzzle, 64, forward, end);
gi.trace ( &tr, muzzle, NULL, NULL, end, borg->s.number, MASK_SHOT );
if ( tr.surfaceFlags & SURF_NOIMPACT )
return;
traceEnt = &g_entities[ tr.entityNum ];
//FIXME: generate effect and sound
if ( traceEnt->takedamage && traceEnt->client && traceEnt->client->ps.stats[STAT_HEALTH] > 0)
{
//You've been assimilated!
float damage = ( g_spskill->integer == 2 ) ? 5.0f : 1.0f;
G_Damage( traceEnt, borg, borg, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_ASSIMILATE );
}
//Basically, longer on easy, less on hard
int attackDelay = ( ( 2 - ( ( g_spskill->integer ) % 3 ) ) + 1 ) * BORG_ATTACK_DELAY;
borg->attackDebounceTime = level.time + attackDelay;
}
//---------------------------------------------------------
void WP_FireBorgTaser( gentity_t *ent )
//---------------------------------------------------------
{
trace_t tr;
vec3_t end, d_dir;
gentity_t *tent;
gentity_t *traceEnt;
VectorMA (muzzle, weaponData[WP_BORG_TASER].range, forward, end);
gi.trace ( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT);
if ( tr.surfaceFlags & SURF_NOIMPACT )
{
return;
}
traceEnt = &g_entities[ tr.entityNum ];
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
tent = G_TempEntity( tr.endpos, EV_BTASER_HIT );
}
else
{
tent = G_TempEntity( tr.endpos, EV_BTASER_MISS );
}
VectorCopy( muzzle, tent->s.origin2 );
tent->s.eventParm = DirToByte( tr.plane.normal );
tent->s.weapon = ent->s.weapon;
if ( ent->NPC_type && Q_stricmp( "satan", ent->NPC_type ) == 0 )
{//satan's robot- more damage, greyscale effect
tent->count = 1;
}
if ( traceEnt->takedamage)
{
//For knockback - send them up in air
VectorCopy(forward, d_dir);
if(d_dir[2] < 0.30f)
{
d_dir[2] = 0.30f;
}
VectorNormalize(d_dir);
G_Damage( traceEnt, ent, ent, d_dir, tr.endpos, 5, 0, MOD_TASER );
}
}
/*
----------------------------------------------
SCOUT BOT
----------------------------------------------
*/
#define BOT_WELDER_RANGE 128
#define BOT_WELDER_DAMAGE_EASY 4
#define BOT_WELDER_DAMAGE_NORMAL 8
#define BOT_WELDER_DAMAGE_HARD 10
#define BOT_WELDER_SPEED_EASY 500
#define BOT_WELDER_SPEED_NORMAL 650
#define BOT_WELDER_SPEED_HARD 800
#define BOT_LASER_DAMAGE 5
#define BOT_LASER_RANGE 1024
//---------------------------------------------------------
void WP_BotWelder( gentity_t *ent )
//---------------------------------------------------------
{
gentity_t *bolt;
vec3_t dir, start;
VectorCopy( forward, dir );
VectorCopy( muzzle, start );
VectorNormalize (dir);
bolt = G_Spawn();
bolt->classname = "bot_projectile";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_BOT_WELDER;
bolt->owner = ent;
if ( g_spskill->integer == 0 )
bolt->damage = BOT_WELDER_DAMAGE_EASY;
else if ( g_spskill->integer == 1 )
bolt->damage = BOT_WELDER_DAMAGE_NORMAL;
else
bolt->damage = BOT_WELDER_DAMAGE_HARD;
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->methodOfDeath = MOD_ENERGY;
bolt->clipmask = MASK_SHOT;
// How 'bout we give this thing a size...
// VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f );
// VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
float speed;
if ( g_spskill->integer == 0 )
speed = BOT_WELDER_SPEED_EASY;
else if ( g_spskill->integer == 1 )
speed = BOT_WELDER_SPEED_NORMAL;
else
speed = BOT_WELDER_SPEED_HARD;
VectorScale( dir, speed, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->currentOrigin);
VectorCopy( start, bolt->pos1 );
}
void WP_BotLaser( gentity_t *ent )
//---------------------------------------------------------
{//Short burst laser, works like a long range lightning gun (held fire)
trace_t tr;
vec3_t end;
gentity_t *traceEnt;//, *tent;
VectorMA( muzzle, BOT_LASER_RANGE, forward, end );
gi.trace( &tr, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );
if ( tr.entityNum == ENTITYNUM_NONE )
{
return;
}
traceEnt = &g_entities[ tr.entityNum ];
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
/* tent = G_TempEntity( tr.endpos, EV_DREADNOUGHT_HIT );
tent->s.otherEntityNum = traceEnt->s.number;
tent->s.eventParm = DirToByte( tr.plane.normal );
tent->s.weapon = ent->s.weapon;
*/
}
else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) )
{
/* tent = G_TempEntity( tr.endpos, EV_DREADNOUGHT_MISS );
tent->s.eventParm = DirToByte( tr.plane.normal );
*/
}
if ( traceEnt->takedamage)
{
G_Damage( traceEnt, ent, ent, forward, tr.endpos, BOT_LASER_DAMAGE, 0, MOD_TARGET_LASER );
}
if ( !ent->s.loopSound )
{
G_Sound( ent, G_SoundIndex( "sound/weapons/scout_bot/laser_start.wav" ) );
ent->s.loopSound = G_SoundIndex( "sound/weapons/scout_bot/laser_firing.wav" );
}
}
/*
----------------------------------------------
HUNTER SEEKER
----------------------------------------------
*/
#define BOT_ROCKET_DAMAGE 10
#define BOT_ROCKET_SPLASH_RAD 64
#define BOT_ROCKET_SPLASH_DAM 8
#define BOT_ROCKET_SPEED 250
#define BOT_ROCKET_CORRECT_AMT 0.05f // (0.0f - 1.0f) -- higher values are better
#define BOT_ROCKET_DRUNK_AMT 0.009f
void bot_rocket_think( gentity_t *ent )
//---------------------------------------------------------
{
vec3_t newdir, targetdir, dir, org;
float dot;
if ( ent->owner && ent->owner->enemy )
{
// Already have a target, start homing.
if ( !ent->owner->enemy->inuse || ent->owner->enemy->health <= 0 )
{
// No longer target this.
ent->owner = NULL;
ent->nextthink = level.time + 1000;
return;
}
AngleVectors( ent->owner->enemy->client->ps.viewangles, dir, NULL, NULL );
dot = DotProduct( dir, ent->movedir );
// If the thing gets behind us, don't track anymore
if ( ( dot < -0.5f ) || ( ent->spawnflags & 2 ) )
{
VectorCopy( ent->owner->enemy->currentOrigin, org );
org[2] += 22;
VectorSubtract( org, ent->currentOrigin, targetdir );
VectorNormalize( targetdir );
// Do some direction correction
if ( ent->spawnflags & 2 )
{
VectorMA( ent->movedir, BOT_ROCKET_CORRECT_AMT * 4, targetdir, newdir );
}
else
{
VectorMA( ent->movedir, BOT_ROCKET_CORRECT_AMT * (g_spskill->integer + 1), targetdir, newdir );
}
// Make the rocket act a bit "drunk"
for ( int i = 0; i < 3; i++ )
{
newdir[i] += crandom() * BOT_ROCKET_DRUNK_AMT * (g_spskill->integer + 1) * (g_spskill->integer + 1);
}
VectorNormalize( newdir );
if ( ent->spawnflags & 2 )
{
VectorScale( newdir, 400, ent->s.pos.trDelta );
}
else
{
VectorScale( newdir, ( g_spskill->integer ) * 75 + BOT_ROCKET_SPEED, ent->s.pos.trDelta );
}
VectorCopy( newdir, ent->movedir );
SnapVector( ent->s.pos.trDelta ); // save net bandwidth
VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
ent->s.pos.trTime = level.time;
}
}
ent->nextthink = level.time + 100;
}
//---------------------------------------------------------
void bot_rocket_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod )
{
gentity_t *tent;
// Death
tent = G_TempEntity(self->currentOrigin, EV_BOT_ROCKET_DIE);
VectorAdd( self->currentOrigin, self->movedir, tent->s.origin2 );
// remove the rocket after a bit
self->e_ThinkFunc = thinkF_G_FreeEntity;
self->nextthink = level.time + 100;
}
void WP_BotRocket( gentity_t *ent )
//---------------------------------------------------------
{//Rocket projectile
gentity_t *bolt;
vec3_t start;
VectorMA( ent->currentOrigin, 6, forward, start );
VectorMA( start, 2, up, start );
bolt = G_Spawn();
VectorSet( bolt->mins, -4, -4, -4 );
VectorSet( bolt->maxs, 4, 4, 4 );
bolt->classname = "bot_rocket";
bolt->nextthink = level.time + 200;
bolt->e_ThinkFunc = thinkF_bot_rocket_think;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_BOT_ROCKET;
bolt->owner = ent;
bolt->damage = BOT_ROCKET_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = BOT_ROCKET_SPLASH_DAM;
bolt->splashRadius = BOT_ROCKET_SPLASH_RAD;
bolt->methodOfDeath = MOD_BOTROCKET;
bolt->splashMethodOfDeath = MOD_BOTROCKET_SPLASH;
bolt->clipmask = MASK_SHOT;
bolt->contents = CONTENTS_SOLID;
bolt->takedamage = qtrue;
bolt->health = 10;
bolt->e_DieFunc = dieF_bot_rocket_die;
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( forward, BOT_ROCKET_SPEED, bolt->s.pos.trDelta );
VectorCopy( forward, bolt->movedir );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( start, bolt->currentOrigin );
VectorCopy( start, bolt->pos1 );
gi.linkentity( bolt );
}
void WP_BotTurret( gentity_t * ent )
//---------------------------------------------------------
{
vec3_t end;
trace_t tr;
gentity_t *tr_ent;
VectorMA( ent->client->renderInfo.muzzlePoint, 1024, ent->pos2, end );
gi.trace( &tr, ent->client->renderInfo.muzzlePoint, NULL, NULL, end, ent->s.number, MASK_SHOT );
tr_ent = &g_entities[ tr.entityNum ];
G_Damage( tr_ent, ent, ent, ent->pos2, tr.endpos, 3, 0, MOD_TARGET_LASER );
}
void WP_HunterSeeker( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
if ( alt_fire )
WP_BotTurret( ent );
else
WP_BotRocket( ent );
}
/*
----------------------------------------------
REAVER
----------------------------------------------
*/
#define FORGE_PROJECTILE_DAMAGE 10
#define FORGE_PROJECTILE_SPLASH_RAD 64
#define FORGE_PROJECTILE_SPLASH_DAM 8
#define FORGE_PROJECTILE_VELOCITY 750
void WP_ForgeProjectile( gentity_t *ent )
//---------------------------------------------------------
{//Energy projectile weapon
gentity_t *bolt;
bolt = G_Spawn();
bolt->classname = "forge_projectile";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_FORGE_PROJ;
bolt->owner = ent;
bolt->damage = FORGE_PROJECTILE_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = FORGE_PROJECTILE_SPLASH_DAM;
bolt->splashRadius = FORGE_PROJECTILE_SPLASH_RAD;
bolt->methodOfDeath = MOD_ENERGY;
bolt->splashMethodOfDeath = MOD_ENERGY_SPLASH;
bolt->clipmask = MASK_SHOT;
// How 'bout we give this thing a size...
VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f );
VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( muzzle, bolt->s.pos.trBase );
VectorScale( forward, FORGE_PROJECTILE_VELOCITY, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( muzzle, bolt->currentOrigin );
}
/*
----------------------------------------------
AVATAR
----------------------------------------------
*/
#define PSYCHIC_BLAST_DAMAGE 12
#define PSYCHIC_BLAST_SPLASH_DAM 5
#define PSYCHIC_BLAST_SPLASH_RAD 32
#define PSYCHIC_BLAST_VELOCITY 800
void WP_ForgePsychicBlast( gentity_t *ent )
//---------------------------------------------------------
{
gentity_t *bolt;
bolt = G_Spawn();
bolt->classname = "psychic_projectile";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_FORGE_PSYCH;
bolt->owner = ent;
bolt->damage = PSYCHIC_BLAST_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = PSYCHIC_BLAST_SPLASH_DAM;
bolt->splashRadius = PSYCHIC_BLAST_SPLASH_RAD;
bolt->methodOfDeath = MOD_ENERGY;
bolt->splashMethodOfDeath = MOD_ENERGY_SPLASH;
bolt->clipmask = MASK_SHOT;
// How 'bout we give this thing a size...
VectorSet( bolt->mins, -2.0f, -2.0f, -2.0f );
VectorSet( bolt->maxs, 2.0f, 2.0f, 2.0f );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( muzzle, bolt->s.pos.trBase );
VectorScale( forward, PSYCHIC_BLAST_VELOCITY, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( muzzle, bolt->currentOrigin );
}
/*
----------------------------------------------
PARASITE
----------------------------------------------
*/
#define PARASITE_DAMAGE 6
#define PARASITE_SPLASH_DAM 4
#define PARASITE_SPLASH_RAD 72
#define PARASITE_VELOCITY 500
#define PARASITE_UPWARD_KICK 200
//---------------------------------------------------------
void WP_ParasiteAcid( gentity_t *ent )
//---------------------------------------------------------
{
//shoots acid spurts at player
gentity_t *bolt;
bolt = G_Spawn();
bolt->classname = "acid_projectile";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_PARASITE;
bolt->owner = ent;
bolt->damage = PARASITE_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = PARASITE_SPLASH_DAM;
bolt->splashRadius = PARASITE_SPLASH_RAD;
bolt->methodOfDeath = MOD_ACID;
bolt->splashMethodOfDeath = MOD_ACID_SPLASH;
bolt->clipmask = MASK_SHOT;
bolt->s.pos.trType = TR_GRAVITY;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( muzzle, bolt->s.pos.trBase );
VectorScale( forward, PARASITE_VELOCITY, bolt->s.pos.trDelta );
bolt->s.pos.trDelta[2] += PARASITE_UPWARD_KICK; // give the spurt a bit of an extra upward thrust so it'll travel a bit further.
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( muzzle, bolt->currentOrigin );
}
/*
----------------------------------------------
STASIS ALIEN
----------------------------------------------
*/
#define STASIS_ALIEN_VELOCITY 450
#define STASIS_ALIEN_DAMAGE 5
//---------------------------------------------------------
void FireStasisGuyAttack( gentity_t *ent )
//---------------------------------------------------------
{
gentity_t *bolt;
vec3_t dir, start;
VectorSet( dir, 0,0,1 ); // up
// Move the shot up by his hands
VectorMA( muzzle, 24, dir, start );
VectorCopy( forward, dir );
VectorNormalize (dir);
bolt = G_Spawn();
bolt->classname = "stasis_alien_proj";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
bolt->s.weapon = WP_STASIS_ATTACK;
bolt->owner = ent;
bolt->damage = STASIS_ALIEN_DAMAGE;
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->methodOfDeath = MOD_STASIS;
bolt->clipmask = MASK_SHOT;
// How 'bout we give this thing a size...
VectorSet( bolt->mins, -4.0f, -4.0f, -4.0f );
VectorSet( bolt->maxs, 4.0f, 4.0f, 4.0f );
bolt->s.pos.trType = TR_LINEAR;
bolt->s.pos.trTime = level.time;
VectorCopy( start, bolt->s.pos.trBase );
VectorScale( dir, STASIS_ALIEN_VELOCITY, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy (start, bolt->currentOrigin);
}
//---------------------------------------------------------
extern void Q3_SetVar( int taskID, int entID, const char *type_name, const char *data );
void G_HypoUsedHack( gentity_t *ent )
{
//HACKHACKHACK
//switch back to scav rifle after one use
if ( ent->client->ps.stats[STAT_WEAPONS]&(1<<WP_SCAVENGER_RIFLE) )
{
CG_ChangeWeapon( WP_SCAVENGER_RIFLE );
}
else
{
CG_ChangeWeapon( WP_PHASER );
}
//remove the weapon from the inventory
ent->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_BLUE_HYPO );
ent->client->ps.stats[STAT_WEAPONS] &= ~( 1 << WP_RED_HYPO );
//clear this variable
Q3_SetVar( -1, 0, "whichhypo", "NULL" );
//HACKHACKHACK
}
void WP_SprayBlueHypo( gentity_t *ent )
{
gentity_t *tr_ent;
trace_t tr;
vec3_t mins, maxs, end;
// Move out to the end of the nozzle
//VectorMA( muzzle, 20, forward, muzzle );
//VectorMA( muzzle, 4, vright, muzzle );
VectorMA( muzzle, 24, forward, end );
VectorSet( maxs, 6, 6, 6 );
VectorScale( maxs, -1, mins );
gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
G_HypoUsedHack( ent );
if ( tr.entityNum >= ENTITYNUM_WORLD )
{
gentity_t *t_ent;
// Create the effect -- thought something was needed here, but apparently not.
VectorMA( muzzle, 20, forward, muzzle );
VectorMA( muzzle, 4, vright, muzzle );
t_ent = G_TempEntity( muzzle, EV_HYPO_PUFF );
t_ent->s.eventParm = qfalse;
VectorCopy( forward, t_ent->pos1 );
return;
}
tr_ent = &g_entities[tr.entityNum];
// This should only work on things that have a client
if ( tr_ent->client )
{
G_Damage( tr_ent, ent, ent, forward, tr.endpos, 0, DAMAGE_NO_DAMAGE, MOD_UNKNOWN );
//GEntity_PainFunc( tr_ent, ent, 1 );
}
}
//---------------------------------------------------------
void WP_SprayRedHypo( gentity_t *ent )
{
gentity_t *tr_ent;
trace_t tr;
vec3_t mins, maxs, end;
// Move out to the end of the nozzle
//VectorMA( muzzle, 20, forward, muzzle );
//VectorMA( muzzle, 4, vright, muzzle );
VectorMA( muzzle, 24, forward, end );
VectorSet( maxs, 6, 6, 6 );
VectorScale( maxs, -1, mins );
gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
G_HypoUsedHack( ent );
if ( tr.entityNum >= ENTITYNUM_WORLD )
{
gentity_t *t_ent;
// Create the effect -- thought something was needed here, but apparently not.
VectorMA( muzzle, 20, forward, muzzle );
VectorMA( muzzle, 4, vright, muzzle );
t_ent = G_TempEntity( muzzle, EV_HYPO_PUFF );
t_ent->s.eventParm = qtrue;
VectorCopy( forward, t_ent->pos1 );
return;
}
tr_ent = &g_entities[tr.entityNum];
// This should only work on things that have a client
if ( tr_ent && tr_ent->client )
{
if ( tr_ent->NPC_type && tr_ent->NPC_type[0] && Q_stricmp( tr_ent->NPC_type, "hirogenalpha" ) == 0 )
{//If scavboss, just fire his target2
G_UseTargets2( tr_ent, tr_ent, tr_ent->target2 );
tr_ent->target2 = NULL;
//so he doesn't repeat himself
if ( tr_ent->NPC )
{
tr_ent->NPC->localState = 4;
}
//take some health away
tr_ent->health -= 25;
if ( tr_ent->health < 1 )
{//but don't kill him
tr_ent->health = 1;
}
}
else
{
//knock them out, no deathscript, no deathtarget
tr_ent->behaviorSet[BSET_DEATH] = NULL;
tr_ent->behaviorSet[BSET_FFDEATH] = NULL;
tr_ent->target = NULL;
tr_ent->health = 0;
if ( tr_ent->client->playerTeam == TEAM_STARFLEET )
{//"killed" a teammate, you lose
killPlayerTimer = level.time + 5000;
statusTextIndex = IGT_YOUCAUSEDDEATHOFTEAMMATE;
}
player_die( tr_ent, ent, ent, 0, MOD_KNOCKOUT );
//G_Damage ( tr_ent, NULL, NULL, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_TELEFRAG );
}
}
}
//---------------------------------------------------------
void WP_SprayVoyagerHypo( gentity_t *ent )
{
gentity_t *tr_ent;
trace_t tr;
vec3_t mins, maxs, end;
VectorMA( muzzle, 32, forward, end );
VectorSet( maxs, 6, 6, 6 );
VectorScale( maxs, -1, mins );
gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
if ( tr.entityNum >= ENTITYNUM_WORLD )
{
gentity_t *t_ent;
// Create the effect -- thought something was needed here, but apparently not.
VectorMA( muzzle, 20, forward, muzzle );
VectorMA( muzzle, 4, vright, muzzle );
t_ent = G_TempEntity( muzzle, EV_HYPO_PUFF );
t_ent->s.eventParm = qfalse;
VectorCopy( forward, t_ent->pos1 );
return;
}
tr_ent = &g_entities[tr.entityNum];
// Not exactly sure the best way to do this, but ah well...
if ( tr_ent && tr_ent->health > 0 )
{
if ( tr_ent->health > tr_ent->max_health - 20 )
{
tr_ent->health = tr_ent->max_health;
}
else
{
tr_ent->health += 20;
}
}
}
//---------------------------------------------------------
void WP_KnifeSwing( gentity_t *ent )
{
gentity_t *tr_ent;
trace_t tr;
vec3_t mins, maxs, end;
VectorMA( muzzle, 64, forward, end );
VectorSet( maxs, 6, 6, 6 );
VectorScale( maxs, -1, mins );
gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
if ( tr.entityNum >= ENTITYNUM_WORLD )
{
return;
}
tr_ent = &g_entities[tr.entityNum];
if ( tr_ent && tr_ent->takedamage )
{
G_Sound( tr_ent, G_SoundIndex( va("sound/enemies/harvester/stab%d", Q_irand(1, 3)) ) );
G_Damage( tr_ent, ent, ent, forward, tr.endpos, (g_spskill->integer*2)+1, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
}
}
#define PALADIN_VELOCITY 700
#define PALADIN_DAMAGE 4
//---------------------------------------------------------
void WP_PaladinShot( gentity_t *ent )
{
gentity_t *bolt;
bolt = G_Spawn();
bolt->classname = "paladin_proj";
bolt->nextthink = level.time + 10000;
bolt->e_ThinkFunc = thinkF_G_FreeEntity;
bolt->s.eType = ET_MISSILE;
bolt->s.weapon = WP_PALADIN;
bolt->owner = ent;
bolt->damage = PALADIN_DAMAGE*(g_spskill->integer+1);
bolt->dflags = 0;
bolt->splashDamage = 0;
bolt->splashRadius = 0;
bolt->methodOfDeath = MOD_SCAVENGER;
bolt->clipmask = MASK_SHOT;
bolt->s.pos.trType = TR_LINEAR ;
bolt->s.pos.trTime = level.time; // move a bit on the very first frame
VectorCopy( muzzle, bolt->s.pos.trBase );
VectorScale( forward, PALADIN_VELOCITY, bolt->s.pos.trDelta );
SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
VectorCopy( muzzle, bolt->currentOrigin);
}
#define DESPERADO_DAMAGE 3
//---------------------------------------------------------
void WP_DesperadoShot( gentity_t *ent )
{
gentity_t *tent;
gentity_t *tent2 = 0;
gentity_t *traceEnt;
trace_t tr;
vec3_t start, end;
qboolean do_damage = qtrue;
qboolean render_impact = qtrue;
VectorCopy( muzzle, start );
VectorMA( start, -8, forward, start );
VectorMA( start, 8192, forward, end );
gi.trace ( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT );
// If the beam hits a skybox, etc. it would look foolish to add in an explosion
if ( tr.surfaceFlags & SURF_NOIMPACT )
render_impact = qfalse;
traceEnt = &g_entities[ tr.entityNum ];
tent = G_TempEntity( tr.endpos, EV_RIFLE );
// Only add in impact stuff when told to do so
if ( render_impact )
{
// send bullet impact
if ( traceEnt->takedamage && traceEnt->client )
{
// Create a simple impact type mark
tent2 = G_TempEntity( tr.endpos, EV_RIFLE_HIT );
tent->s.otherEntityNum = traceEnt->s.number;
}
else
{
// Do a discreet miss
tent2 = G_TempEntity( tr.endpos, EV_RIFLE_MISS );
}
}
// Stash origins, etc. so that the effect can have access to them.
VectorCopy( muzzle, tent->s.origin2 );
if ( render_impact )
{
VectorCopy( muzzle, tent2->s.origin2 );
tent2->s.eventParm = tent->s.eventParm = DirToByte( tr.plane.normal );
tent2->s.weapon = tent->s.weapon = ent->s.weapon;
}
if ( traceEnt->takedamage && do_damage )
G_Damage( traceEnt, ent, ent, forward, tr.endpos, DESPERADO_DAMAGE*(g_spskill->integer+1), 0, MOD_CRIFLE );
}
//---------------------------------------------------------
void AddLeanOfs(gentity_t *ent, vec3_t point)
//---------------------------------------------------------
{
if(ent->client)
{
if(ent->client->ps.leanofs)
{
vec3_t right;
//add leaning offset
AngleVectors(ent->client->ps.viewangles, NULL, right, NULL);
VectorMA(point, (float)ent->client->ps.leanofs, right, point);
}
}
}
//---------------------------------------------------------
void ViewHeightFix(gentity_t *ent)
//---------------------------------------------------------
{
//FIXME: this is hacky and doesn't need to be here. Was only put here to make up
//for the times a crouch anim would be used but not actually crouching.
//When we start calcing eyepos (SPOT_HEAD) from the tag_eyes, we won't need
//this (or viewheight at all?)
if ( !ent->client || !ent->NPC )
return;
if ( ent->client->ps.stats[STAT_HEALTH] <= 0 )
return;//dead
if ( ( ent->client->ps.legsAnim&~ANIM_TOGGLEBIT) == BOTH_CROUCH1IDLE || (ent->client->ps.legsAnim&~ANIM_TOGGLEBIT) == BOTH_CROUCH1 || (ent->client->ps.legsAnim&~ANIM_TOGGLEBIT ) == BOTH_CROUCH1WALK )
{
if ( ent->client->ps.viewheight!=ent->client->crouchheight + STANDARD_VIEWHEIGHT_OFFSET )
ent->client->ps.viewheight = ent->client->crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
}
else
{
if ( ent->client->ps.viewheight!=ent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET )
ent->client->ps.viewheight = ent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET;
}
}
/*
===============
LogAccuracyHit
===============
*/
qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
if( !target->takedamage ) {
return qfalse;
}
if ( target == attacker ) {
return qfalse;
}
if( !target->client ) {
return qfalse;
}
if( !attacker->client ) {
return qfalse;
}
if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
return qfalse;
}
if ( OnSameTeam( target, attacker ) ) {
return qfalse;
}
return qtrue;
}
//---------------------------------------------------------
void CalcMuzzlePoint( gentity_t *ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint, float lead_in )
//---------------------------------------------------------
{
if((ent->client->ps.weapon != WP_BORG_ASSIMILATOR) && (!lead_in)) //&& ent->s.number != 0
{//Not players or melee
if( ent->client )
{
if ( ent->client->renderInfo.mPCalcTime >= level.time - FRAMETIME*2 )
{//Our muzz point was calced no more than 2 frames ago
VectorCopy(ent->client->renderInfo.muzzlePoint, muzzlePoint);
return;
}
}
}
VectorCopy( ent->currentOrigin, muzzlePoint );
switch( ent->s.weapon )
{
case WP_IMOD:
muzzlePoint[2] += ent->client->ps.viewheight;//By eyes
muzzlePoint[2] -= 13;
VectorMA( muzzlePoint, 26, forward, muzzlePoint );
VectorMA( muzzlePoint, 6, vright, muzzlePoint );
break;
case WP_COMPRESSION_RIFLE:
case WP_RED_HYPO:
case WP_BLUE_HYPO:
case WP_VOYAGER_HYPO:
case WP_TETRION_DISRUPTOR:
case WP_PROTON_GUN:
ViewHeightFix(ent);
muzzlePoint[2] += ent->client->ps.viewheight;//By eyes
muzzlePoint[2] -= 16;
VectorMA( muzzlePoint, 28, forward, muzzlePoint );
VectorMA( muzzlePoint, 6, vright, muzzlePoint );
break;
case WP_STASIS:
case WP_QUANTUM_BURST:
case WP_GRENADE_LAUNCHER:
ViewHeightFix(ent);
muzzlePoint[2] += ent->client->ps.viewheight;//By eyes
muzzlePoint[2] -= 2;
break;
case WP_SCAVENGER_RIFLE:
case WP_CHAOTICA_GUARD_GUN:
ViewHeightFix(ent);
muzzlePoint[2] += ent->client->ps.viewheight;//By eyes
muzzlePoint[2] -= 1;
if ( ent->s.number == 0 )
VectorMA( muzzlePoint, 12, forward, muzzlePoint ); // player, don't set this any lower otherwise the projectile will impact immediately when your back is to a wall
else
VectorMA( muzzlePoint, 2, forward, muzzlePoint ); // NPC, don't set too far forward otherwise the projectile can go through doors
VectorMA( muzzlePoint, 1, vright, muzzlePoint );
break;
case WP_BORG_TASER:
case WP_BORG_WEAPON:
case WP_PHASER:
if(ent->NPC!=NULL &&
(ent->client->ps.torsoAnim&~ANIM_TOGGLEBIT == TORSO_WEAPONREADY2 ||
ent->client->ps.torsoAnim&~ANIM_TOGGLEBIT == BOTH_ATTACK2))//Sniper pose
{
ViewHeightFix(ent);
muzzle[2] += ent->client->ps.viewheight;//By eyes
}
else
{
muzzlePoint[2] += 16;
}
VectorMA( muzzlePoint, 8, forward, muzzlePoint );
VectorMA( muzzlePoint, 16, vright, muzzlePoint );
break;
case WP_BORG_ASSIMILATOR:
ViewHeightFix(ent);
muzzlePoint[2] += ent->maxs[2];//ent->client->ps.viewheight;//By eyes
VectorMA( muzzlePoint, 8, forward, muzzlePoint );
VectorMA( muzzlePoint, -4, vright, muzzlePoint );
break;
case WP_PARASITE:
muzzlePoint[2] -= 23;
break;
case WP_BOT_WELDER:
muzzlePoint[2] -= 8; //Near the naughty-bits
break;
case WP_BOT_ROCKET:
break;
}
AddLeanOfs(ent, muzzlePoint);
}
//---------------------------------------------------------
void FireWeapon( gentity_t *ent, qboolean alt_fire )
//---------------------------------------------------------
{
// track shots taken for accuracy tracking.
ent->client->ps.persistant[PERS_ACCURACY_SHOTS]++;
// set aiming directions
AngleVectors (ent->client->ps.viewangles, forward, vright, up);
ent->alt_fire = alt_fire;
CalcMuzzlePoint ( ent, forward, vright, up, muzzle , 0);
// fire the specific weapon
switch( ent->s.weapon )
{
// Player weapons
//-----------------
case WP_PHASER:
WP_FirePhaser( ent, alt_fire );
break;
case WP_COMPRESSION_RIFLE:
WP_FireCompressionRifle( ent, alt_fire );
break;
case WP_IMOD:
WP_FireIMOD( ent, alt_fire );
break;
case WP_SCAVENGER_RIFLE:
WP_FireScavenger( ent, alt_fire, qfalse );
break;
case WP_CHAOTICA_GUARD_GUN:
WP_FireScavenger( ent, qfalse, qtrue );
break;
case WP_STASIS:
WP_FireStasis( ent, alt_fire );
break;
case WP_GRENADE_LAUNCHER:
WP_FireGrenade( ent, alt_fire );
break;
case WP_TETRION_DISRUPTOR:
WP_FireTetrionDisruptor( ent, alt_fire );
break;
case WP_DREADNOUGHT:
WP_FireDreadnought( ent, alt_fire );
break;
case WP_QUANTUM_BURST:
WP_FireQuantumBurst( ent, alt_fire );
break;
case WP_TRICORDER:
WP_TricorderScan( ent, alt_fire );
break;
case WP_PROTON_GUN:
WP_FireProtonGun( ent, qfalse );//no alt-fire
break;
// Borg Weapons
//-----------------
case WP_BORG_ASSIMILATOR:
case WP_BORG_DRILL:
WP_BorgAssimilate( ent );
break;
case WP_BORG_TASER:
WP_FireBorgTaser( ent );
break;
case WP_BORG_WEAPON:
WP_FireBorgWeapon( ent );
break;
// Scout Bot
//-----------------
case WP_BOT_WELDER:
WP_BotWelder( ent );
break;
/*
case WP_BOT_LASER:
WP_BotLaser( ent );
break;
*/
// Hunter Seeker
//-----------------
case WP_BOT_ROCKET:
WP_HunterSeeker( ent, alt_fire );
break;
// Reaver
//-----------------
case WP_FORGE_PROJ:
WP_ForgeProjectile( ent );
break;
// Avatar
//-----------------
case WP_FORGE_PSYCH:
WP_ForgePsychicBlast( ent );
break;
// Parasite
//-----------------
case WP_PARASITE:
WP_ParasiteAcid( ent );
break;
// Melee Placeholder, doesn't do much of anything as you can tell
//-----------------
case WP_MELEE:
break;
// Stasis Bad Boy
//-----------------
case WP_STASIS_ATTACK:
FireStasisGuyAttack( ent );
break;
// Fun hypo "weapons"
//-----------------
case WP_BLUE_HYPO:
WP_SprayBlueHypo( ent );
break;
case WP_RED_HYPO:
WP_SprayRedHypo( ent );
break;
case WP_VOYAGER_HYPO:
WP_SprayVoyagerHypo( ent );
break;
// Holo-character weapons
//-----------------
case WP_PALADIN:
WP_PaladinShot( ent );
break;
case WP_DESPERADO:
WP_DesperadoShot( ent );
break;
case WP_KLINGON_BLADE:
case WP_IMPERIAL_BLADE:
WP_KnifeSwing( ent );
break;
default:
break;
}
}
//DETPACK
//---------------------------------------------------------
void UseCharge (int entityNum)
//---------------------------------------------------------
{
gentity_t *ent;
if(entityNum < 0 || entityNum >= ENTITYNUM_WORLD)
{
return;
}
ent = &g_entities[entityNum];
if(!ent || !ent->client)
{
return;
}
if(ent->client->ps.eFlags & EF_PLANTED_CHARGE)
{
gentity_t *found = NULL;
G_Sound(ent, G_SoundIndex("sound/weapons/detpacklatch.wav"));
while((found = G_Find(found, FOFS(classname), "charge")) != NULL)
{
if(found->owner == ent)
{
VectorCopy( found->currentOrigin, found->s.origin );
found->e_ThinkFunc = thinkF_ExplodeDeath;
found->nextthink = level.time + 500;
G_Sound(found, G_SoundIndex("sound/weapons/detpackfire.wav"));
//Fixme: this should really wake up enemies in the area
}
}
ent->client->ps.eFlags &= ~EF_PLANTED_CHARGE;
}
else
{
AngleVectors (ent->client->ps.viewangles, forward, vright, up);
CalcMuzzlePoint ( ent, forward, vright, up, muzzle, 0 );
VectorNormalize( forward );
VectorMA(muzzle, -4, forward, muzzle);
drop_charge (ent, muzzle, forward);
ent->client->ps.eFlags |= EF_PLANTED_CHARGE;
}
}