699 lines
19 KiB
C
699 lines
19 KiB
C
// Copyright (C) 1999-2000 Id Software, Inc.
|
|
//
|
|
#include "g_local.h"
|
|
|
|
#define MISSILE_PRESTEP_TIME 50
|
|
|
|
extern void laserTrapStick( gentity_t *ent, vec3_t endpos, vec3_t normal );
|
|
|
|
/*
|
|
================
|
|
G_ReflectMissile
|
|
|
|
Reflect the missile roughly back at it's owner
|
|
================
|
|
*/
|
|
float RandFloat(float min, float max);
|
|
void G_ReflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward )
|
|
{
|
|
vec3_t bounce_dir;
|
|
int i;
|
|
float speed;
|
|
gentity_t *owner = ent;
|
|
int isowner = 0;
|
|
|
|
if ( ent->r.ownerNum )
|
|
{
|
|
owner = &g_entities[ent->r.ownerNum];
|
|
}
|
|
|
|
if (missile->r.ownerNum == ent->s.number)
|
|
{ //the original owner is bouncing the missile, so don't try to bounce it back at him
|
|
isowner = 1;
|
|
}
|
|
|
|
//save the original speed
|
|
speed = VectorNormalize( missile->s.pos.trDelta );
|
|
|
|
//if ( ent && owner && owner->NPC && owner->enemy && Q_stricmp( "Tavion", owner->NPC_type ) == 0 && Q_irand( 0, 3 ) )
|
|
if ( &g_entities[missile->r.ownerNum] && missile->s.weapon != WP_SABER && missile->s.weapon != G2_MODEL_PART && !isowner )
|
|
{//bounce back at them if you can
|
|
VectorSubtract( g_entities[missile->r.ownerNum].r.currentOrigin, missile->r.currentOrigin, bounce_dir );
|
|
VectorNormalize( bounce_dir );
|
|
}
|
|
else if (isowner)
|
|
{ //in this case, actually push the missile away from me, and since we're giving boost to our own missile by pushing it, up the velocity
|
|
vec3_t missile_dir;
|
|
|
|
speed *= 1.5;
|
|
|
|
VectorSubtract( missile->r.currentOrigin, ent->r.currentOrigin, missile_dir );
|
|
VectorCopy( missile->s.pos.trDelta, bounce_dir );
|
|
VectorScale( bounce_dir, DotProduct( forward, missile_dir ), bounce_dir );
|
|
VectorNormalize( bounce_dir );
|
|
}
|
|
else
|
|
{
|
|
vec3_t missile_dir;
|
|
|
|
VectorSubtract( ent->r.currentOrigin, missile->r.currentOrigin, missile_dir );
|
|
VectorCopy( missile->s.pos.trDelta, bounce_dir );
|
|
VectorScale( bounce_dir, DotProduct( forward, missile_dir ), bounce_dir );
|
|
VectorNormalize( bounce_dir );
|
|
}
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
bounce_dir[i] += RandFloat( -0.2f, 0.2f );
|
|
}
|
|
|
|
VectorNormalize( bounce_dir );
|
|
VectorScale( bounce_dir, speed, missile->s.pos.trDelta );
|
|
missile->s.pos.trTime = level.time; // move a bit on the very first frame
|
|
VectorCopy( missile->r.currentOrigin, missile->s.pos.trBase );
|
|
if ( missile->s.weapon != WP_SABER && missile->s.weapon != G2_MODEL_PART )
|
|
{//you are mine, now!
|
|
missile->r.ownerNum = ent->s.number;
|
|
}
|
|
if ( missile->s.weapon == WP_ROCKET_LAUNCHER )
|
|
{//stop homing
|
|
missile->think = 0;
|
|
missile->nextthink = 0;
|
|
}
|
|
}
|
|
|
|
void G_DeflectMissile( gentity_t *ent, gentity_t *missile, vec3_t forward )
|
|
{
|
|
vec3_t bounce_dir;
|
|
int i;
|
|
float speed;
|
|
int isowner = 0;
|
|
vec3_t missile_dir;
|
|
|
|
if (missile->r.ownerNum == ent->s.number)
|
|
{ //the original owner is bouncing the missile, so don't try to bounce it back at him
|
|
isowner = 1;
|
|
}
|
|
|
|
//save the original speed
|
|
speed = VectorNormalize( missile->s.pos.trDelta );
|
|
|
|
//VectorSubtract( ent->r.currentOrigin, missile->r.currentOrigin, missile_dir );
|
|
AngleVectors(ent->client->ps.viewangles, missile_dir, 0, 0);
|
|
VectorCopy(missile_dir, bounce_dir);
|
|
//VectorCopy( missile->s.pos.trDelta, bounce_dir );
|
|
VectorScale( bounce_dir, DotProduct( forward, missile_dir ), bounce_dir );
|
|
VectorNormalize( bounce_dir );
|
|
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
bounce_dir[i] += RandFloat( -1.0f, 1.0f );
|
|
}
|
|
|
|
VectorNormalize( bounce_dir );
|
|
VectorScale( bounce_dir, speed, missile->s.pos.trDelta );
|
|
missile->s.pos.trTime = level.time; // move a bit on the very first frame
|
|
VectorCopy( missile->r.currentOrigin, missile->s.pos.trBase );
|
|
if ( missile->s.weapon != WP_SABER && missile->s.weapon != G2_MODEL_PART )
|
|
{//you are mine, now!
|
|
missile->r.ownerNum = ent->s.number;
|
|
}
|
|
if ( missile->s.weapon == WP_ROCKET_LAUNCHER )
|
|
{//stop homing
|
|
missile->think = 0;
|
|
missile->nextthink = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_BounceMissile
|
|
|
|
================
|
|
*/
|
|
void G_BounceMissile( gentity_t *ent, trace_t *trace ) {
|
|
vec3_t velocity;
|
|
float dot;
|
|
int hitTime;
|
|
|
|
// reflect the velocity on the trace plane
|
|
hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction;
|
|
BG_EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity );
|
|
dot = DotProduct( velocity, trace->plane.normal );
|
|
VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );
|
|
|
|
|
|
if ( ent->s.eFlags & EF_BOUNCE_SHRAPNEL )
|
|
{
|
|
VectorScale( ent->s.pos.trDelta, 0.25f, ent->s.pos.trDelta );
|
|
ent->s.pos.trType = TR_GRAVITY;
|
|
|
|
// check for stop
|
|
if ( trace->plane.normal[2] > 0.7 && ent->s.pos.trDelta[2] < 40 ) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7
|
|
{
|
|
G_SetOrigin( ent, trace->endpos );
|
|
ent->nextthink = level.time + 100;
|
|
return;
|
|
}
|
|
}
|
|
else if ( ent->s.eFlags & EF_BOUNCE_HALF )
|
|
{
|
|
VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
|
|
// check for stop
|
|
if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 )
|
|
{
|
|
G_SetOrigin( ent, trace->endpos );
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ent->s.weapon == WP_THERMAL)
|
|
{ //slight hack for hit sound
|
|
G_Sound(ent, CHAN_BODY, G_SoundIndex(va("sound/weapons/thermal/bounce%i.wav", Q_irand(1, 2))));
|
|
}
|
|
else if (ent->s.weapon == WP_SABER)
|
|
{
|
|
G_Sound(ent, CHAN_BODY, G_SoundIndex(va("sound/weapons/saber/bounce%i.wav", Q_irand(1, 3))));
|
|
}
|
|
else if (ent->s.weapon == G2_MODEL_PART)
|
|
{
|
|
//Limb bounce sound?
|
|
}
|
|
|
|
VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
|
|
VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
|
|
ent->s.pos.trTime = level.time;
|
|
|
|
if (ent->bounceCount != -5)
|
|
{
|
|
ent->bounceCount--;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
G_ExplodeMissile
|
|
|
|
Explode a missile without an impact
|
|
================
|
|
*/
|
|
void G_ExplodeMissile( gentity_t *ent ) {
|
|
vec3_t dir;
|
|
vec3_t origin;
|
|
|
|
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
|
|
SnapVector( origin );
|
|
G_SetOrigin( ent, origin );
|
|
|
|
// we don't have a valid direction, so just point straight up
|
|
dir[0] = dir[1] = 0;
|
|
dir[2] = 1;
|
|
|
|
ent->s.eType = ET_GENERAL;
|
|
G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
|
|
|
|
ent->freeAfterEvent = qtrue;
|
|
|
|
// splash damage
|
|
if ( ent->splashDamage ) {
|
|
if( G_RadiusDamage( ent->r.currentOrigin, ent->parent, ent->splashDamage, ent->splashRadius, ent,
|
|
ent->splashMethodOfDeath ) )
|
|
{
|
|
if (ent->parent)
|
|
{
|
|
g_entities[ent->parent->s.number].client->accuracy_hits++;
|
|
}
|
|
else if (ent->activator)
|
|
{
|
|
g_entities[ent->activator->s.number].client->accuracy_hits++;
|
|
}
|
|
}
|
|
}
|
|
|
|
trap_LinkEntity( ent );
|
|
}
|
|
|
|
void G_RunStuckMissile( gentity_t *ent )
|
|
{
|
|
if ( ent->takedamage )
|
|
{
|
|
if ( ent->s.groundEntityNum >= 0 && ent->s.groundEntityNum < ENTITYNUM_WORLD )
|
|
{
|
|
gentity_t *other = &g_entities[ent->s.groundEntityNum];
|
|
|
|
if ( (!VectorCompare( vec3_origin, other->s.pos.trDelta ) && other->s.pos.trType != TR_STATIONARY) ||
|
|
(!VectorCompare( vec3_origin, other->s.apos.trDelta ) && other->s.apos.trType != TR_STATIONARY) )
|
|
{//thing I stuck to is moving or rotating now, kill me
|
|
G_Damage( ent, other, other, NULL, NULL, 99999, 0, MOD_CRUSH );
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// check think function
|
|
G_RunThink( ent );
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_BounceProjectile
|
|
================
|
|
*/
|
|
void G_BounceProjectile( vec3_t start, vec3_t impact, vec3_t dir, vec3_t endout ) {
|
|
vec3_t v, newv;
|
|
float dot;
|
|
|
|
VectorSubtract( impact, start, v );
|
|
dot = DotProduct( v, dir );
|
|
VectorMA( v, -2*dot, dir, newv );
|
|
|
|
VectorNormalize(newv);
|
|
VectorMA(impact, 8192, newv, endout);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
gentity_t *CreateMissile( vec3_t org, vec3_t dir, float vel, int life,
|
|
gentity_t *owner, qboolean altFire)
|
|
//-----------------------------------------------------------------------------
|
|
{
|
|
gentity_t *missile;
|
|
|
|
missile = G_Spawn();
|
|
|
|
missile->nextthink = level.time + life;
|
|
missile->think = G_FreeEntity;
|
|
missile->s.eType = ET_MISSILE;
|
|
missile->r.svFlags = SVF_USE_CURRENT_ORIGIN;
|
|
missile->parent = owner;
|
|
missile->r.ownerNum = owner->s.number;
|
|
|
|
if (altFire)
|
|
{
|
|
missile->s.eFlags |= EF_ALT_FIRING;
|
|
}
|
|
|
|
missile->s.pos.trType = TR_LINEAR;
|
|
missile->s.pos.trTime = level.time;// - MISSILE_PRESTEP_TIME; // NOTENOTE This is a Quake 3 addition over JK2
|
|
missile->target_ent = NULL;
|
|
|
|
SnapVector(org);
|
|
VectorCopy( org, missile->s.pos.trBase );
|
|
VectorScale( dir, vel, missile->s.pos.trDelta );
|
|
VectorCopy( org, missile->r.currentOrigin);
|
|
SnapVector(missile->s.pos.trDelta);
|
|
|
|
return missile;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
G_MissileImpact
|
|
================
|
|
*/
|
|
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
|
|
gentity_t *other;
|
|
qboolean hitClient = qfalse;
|
|
other = &g_entities[trace->entityNum];
|
|
|
|
// check for bounce
|
|
if ( !other->takedamage &&
|
|
(ent->bounceCount > 0 || ent->bounceCount == -5) &&
|
|
( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
|
|
G_BounceMissile( ent, trace );
|
|
G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
|
|
return;
|
|
}
|
|
|
|
// I would glom onto the EF_BOUNCE code section above, but don't feel like risking breaking something else
|
|
if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->s.eFlags&(EF_BOUNCE_SHRAPNEL) ) ) || ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius&&(ent->bounceCount > 0 || ent->bounceCount == -5)) )
|
|
{
|
|
G_BounceMissile( ent, trace );
|
|
|
|
if ( ent->bounceCount < 1 )
|
|
{
|
|
ent->s.eFlags &= ~EF_BOUNCE_SHRAPNEL;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
if ( !other->takedamage && ent->s.weapon == WP_THERMAL && !ent->alt_fire )
|
|
{//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!!
|
|
//G_BounceRollMissile( ent, trace );
|
|
if ( ent->owner && ent->owner->s.number == 0 )
|
|
{
|
|
G_MissileAddAlerts( ent );
|
|
}
|
|
//gi.linkentity( ent );
|
|
return;
|
|
}
|
|
*/
|
|
|
|
if (other->r.contents & CONTENTS_LIGHTSABER)
|
|
{ //hit this person's saber, so..
|
|
gentity_t *otherOwner = &g_entities[other->r.ownerNum];
|
|
|
|
if (otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress &&
|
|
otherOwner->client->ps.duelIndex != ent->r.ownerNum)
|
|
{
|
|
goto killProj;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (other->takedamage && other->client && other->client->ps.duelInProgress &&
|
|
other->client->ps.duelIndex != ent->r.ownerNum)
|
|
{
|
|
goto killProj;
|
|
}
|
|
}
|
|
|
|
if (other->takedamage && other->client &&
|
|
ent->s.weapon != WP_ROCKET_LAUNCHER &&
|
|
ent->s.weapon != WP_THERMAL &&
|
|
ent->s.weapon != WP_TRIP_MINE &&
|
|
ent->s.weapon != WP_DET_PACK &&
|
|
ent->s.weapon != WP_DEMP2 &&
|
|
other->client->ps.saberBlockTime < level.time &&
|
|
WP_SaberCanBlock(other, ent->r.currentOrigin, 0, 0, qtrue, 0))
|
|
{ //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked)
|
|
vec3_t fwd;
|
|
gentity_t *te;
|
|
int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABERDEFEND];
|
|
|
|
te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK );
|
|
VectorCopy(ent->r.currentOrigin, te->s.origin);
|
|
VectorCopy(trace->plane.normal, te->s.angles);
|
|
te->s.eventParm = 0;
|
|
|
|
if (other->client->ps.velocity[2] > 0 ||
|
|
other->client->pers.cmd.forwardmove ||
|
|
other->client->pers.cmd.rightmove)
|
|
{
|
|
otherDefLevel -= 1;
|
|
if (otherDefLevel < 0)
|
|
{
|
|
otherDefLevel = 0;
|
|
}
|
|
}
|
|
|
|
AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL);
|
|
if (otherDefLevel == FORCE_LEVEL_1)
|
|
{
|
|
//if def is only level 1, instead of deflecting the shot it should just die here
|
|
}
|
|
else if (otherDefLevel == FORCE_LEVEL_2)
|
|
{
|
|
G_DeflectMissile(other, ent, fwd);
|
|
}
|
|
else
|
|
{
|
|
G_ReflectMissile(other, ent, fwd);
|
|
}
|
|
other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); //200;
|
|
|
|
if (otherDefLevel == FORCE_LEVEL_3)
|
|
{
|
|
other->client->ps.saberBlockTime = 0; //^_^
|
|
}
|
|
|
|
if (otherDefLevel == FORCE_LEVEL_1)
|
|
{
|
|
goto killProj;
|
|
}
|
|
return;
|
|
}
|
|
else if (other->r.contents & CONTENTS_LIGHTSABER)
|
|
{ //hit this person's saber, so..
|
|
gentity_t *otherOwner = &g_entities[other->r.ownerNum];
|
|
|
|
if (otherOwner->takedamage && otherOwner->client &&
|
|
ent->s.weapon != WP_ROCKET_LAUNCHER &&
|
|
ent->s.weapon != WP_THERMAL &&
|
|
ent->s.weapon != WP_TRIP_MINE &&
|
|
ent->s.weapon != WP_DET_PACK &&
|
|
ent->s.weapon != WP_DEMP2 &&
|
|
ent->methodOfDeath != MOD_REPEATER_ALT &&
|
|
ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH /*&&
|
|
otherOwner->client->ps.saberBlockTime < level.time*/)
|
|
{ //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber
|
|
vec3_t fwd;
|
|
gentity_t *te;
|
|
int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABERDEFEND];
|
|
|
|
//in this case, deflect it even if we can't actually block it because it hit our saber
|
|
WP_SaberCanBlock(otherOwner, ent->r.currentOrigin, 0, 0, qtrue, 0);
|
|
|
|
te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK );
|
|
VectorCopy(ent->r.currentOrigin, te->s.origin);
|
|
VectorCopy(trace->plane.normal, te->s.angles);
|
|
te->s.eventParm = 0;
|
|
|
|
if (otherOwner->client->ps.velocity[2] > 0 ||
|
|
otherOwner->client->pers.cmd.forwardmove ||
|
|
otherOwner->client->pers.cmd.rightmove)
|
|
{
|
|
otherDefLevel -= 1;
|
|
if (otherDefLevel < 0)
|
|
{
|
|
otherDefLevel = 0;
|
|
}
|
|
}
|
|
|
|
AngleVectors(otherOwner->client->ps.viewangles, fwd, NULL, NULL);
|
|
|
|
if (otherDefLevel == FORCE_LEVEL_1)
|
|
{
|
|
//if def is only level 1, instead of deflecting the shot it should just die here
|
|
}
|
|
else if (otherDefLevel == FORCE_LEVEL_2)
|
|
{
|
|
G_DeflectMissile(otherOwner, ent, fwd);
|
|
}
|
|
else
|
|
{
|
|
G_ReflectMissile(otherOwner, ent, fwd);
|
|
}
|
|
otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100));//200;
|
|
|
|
if (otherDefLevel == FORCE_LEVEL_3)
|
|
{
|
|
otherOwner->client->ps.saberBlockTime = 0; //^_^
|
|
}
|
|
|
|
if (otherDefLevel == FORCE_LEVEL_1)
|
|
{
|
|
goto killProj;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// check for sticking
|
|
if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) )
|
|
{
|
|
laserTrapStick( ent, trace->endpos, trace->plane.normal );
|
|
G_AddEvent( ent, EV_MISSILE_STICK, 0 );
|
|
return;
|
|
}
|
|
|
|
// impact damage
|
|
if (other->takedamage) {
|
|
// FIXME: wrong damage direction?
|
|
if ( ent->damage ) {
|
|
vec3_t velocity;
|
|
|
|
if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) {
|
|
g_entities[ent->r.ownerNum].client->accuracy_hits++;
|
|
hitClient = qtrue;
|
|
}
|
|
BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
|
|
if ( VectorLength( velocity ) == 0 ) {
|
|
velocity[2] = 1; // stepped on a grenade
|
|
}
|
|
|
|
if (ent->s.weapon == WP_BOWCASTER || ent->s.weapon == WP_FLECHETTE ||
|
|
ent->s.weapon == WP_ROCKET_LAUNCHER)
|
|
{
|
|
if (ent->s.weapon == WP_FLECHETTE && (ent->s.eFlags & EF_ALT_FIRING))
|
|
{
|
|
ent->think(ent);
|
|
}
|
|
else
|
|
{
|
|
G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
|
|
/*ent->s.origin*/ent->r.currentOrigin, ent->damage,
|
|
DAMAGE_HALF_ABSORB, ent->methodOfDeath);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
|
|
/*ent->s.origin*/ent->r.currentOrigin, ent->damage,
|
|
0, ent->methodOfDeath);
|
|
}
|
|
}
|
|
}
|
|
killProj:
|
|
// is it cheaper in bandwidth to just remove this ent and create a new
|
|
// one, rather than changing the missile into the explosion?
|
|
|
|
if ( other->takedamage && other->client ) {
|
|
G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
|
|
ent->s.otherEntityNum = other->s.number;
|
|
} else if( trace->surfaceFlags & SURF_METALSTEPS ) {
|
|
G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) );
|
|
} else if (ent->s.weapon != G2_MODEL_PART) {
|
|
G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
|
|
}
|
|
|
|
ent->freeAfterEvent = qtrue;
|
|
|
|
// change over to a normal entity right at the point of impact
|
|
ent->s.eType = ET_GENERAL;
|
|
|
|
SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth
|
|
|
|
G_SetOrigin( ent, trace->endpos );
|
|
|
|
// splash damage (doesn't apply to person directly hit)
|
|
if ( ent->splashDamage ) {
|
|
if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
|
|
other, ent->splashMethodOfDeath ) ) {
|
|
if( !hitClient ) {
|
|
g_entities[ent->r.ownerNum].client->accuracy_hits++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ent->s.weapon == G2_MODEL_PART)
|
|
{
|
|
ent->freeAfterEvent = qfalse; //it will free itself
|
|
}
|
|
|
|
trap_LinkEntity( ent );
|
|
}
|
|
|
|
/*
|
|
================
|
|
G_RunMissile
|
|
================
|
|
*/
|
|
void G_RunMissile( gentity_t *ent ) {
|
|
vec3_t origin, groundSpot;
|
|
trace_t tr;
|
|
int passent;
|
|
|
|
// get current position
|
|
BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );
|
|
|
|
// if this missile bounced off an invulnerability sphere
|
|
if ( ent->target_ent ) {
|
|
passent = ent->target_ent->s.number;
|
|
}
|
|
else {
|
|
// ignore interactions with the missile owner
|
|
passent = ent->r.ownerNum;
|
|
}
|
|
// trace a line from the previous position to the current position
|
|
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
|
|
|
|
if ( tr.startsolid || tr.allsolid ) {
|
|
// make sure the tr.entityNum is set to the entity we're stuck in
|
|
trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask );
|
|
tr.fraction = 0;
|
|
}
|
|
else {
|
|
VectorCopy( tr.endpos, ent->r.currentOrigin );
|
|
}
|
|
|
|
if (ent->passThroughNum && tr.entityNum == (ent->passThroughNum-1))
|
|
{
|
|
VectorCopy( origin, ent->r.currentOrigin );
|
|
trap_LinkEntity( ent );
|
|
goto passthrough;
|
|
}
|
|
|
|
trap_LinkEntity( ent );
|
|
|
|
if (ent->s.weapon == G2_MODEL_PART && !ent->bounceCount)
|
|
{
|
|
vec3_t lowerOrg;
|
|
trace_t trG;
|
|
|
|
VectorCopy(ent->r.currentOrigin, lowerOrg);
|
|
lowerOrg[2] -= 1;
|
|
trap_Trace( &trG, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, lowerOrg, passent, ent->clipmask );
|
|
|
|
VectorCopy(trG.endpos, groundSpot);
|
|
|
|
if (!trG.startsolid && !trG.allsolid && trG.entityNum == ENTITYNUM_WORLD)
|
|
{
|
|
ent->s.groundEntityNum = trG.entityNum;
|
|
}
|
|
else
|
|
{
|
|
ent->s.groundEntityNum = ENTITYNUM_NONE;
|
|
}
|
|
}
|
|
|
|
if ( tr.fraction != 1) {
|
|
// never explode or bounce on sky
|
|
if ( tr.surfaceFlags & SURF_NOIMPACT ) {
|
|
// If grapple, reset owner
|
|
if (ent->parent && ent->parent->client && ent->parent->client->hook == ent) {
|
|
ent->parent->client->hook = NULL;
|
|
}
|
|
|
|
if (ent->s.weapon != G2_MODEL_PART)
|
|
{
|
|
G_FreeEntity( ent );
|
|
return;
|
|
}
|
|
}
|
|
G_MissileImpact( ent, &tr );
|
|
if ( ent->s.eType != ET_MISSILE && ent->s.weapon != G2_MODEL_PART ) {
|
|
return; // exploded
|
|
}
|
|
}
|
|
|
|
passthrough:
|
|
if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) )
|
|
{//stuck missiles should check some special stuff
|
|
G_RunStuckMissile( ent );
|
|
return;
|
|
}
|
|
|
|
if (ent->s.weapon == G2_MODEL_PART)
|
|
{
|
|
if (ent->s.groundEntityNum == ENTITYNUM_WORLD)
|
|
{
|
|
ent->s.pos.trType = TR_LINEAR;
|
|
VectorClear(ent->s.pos.trDelta);
|
|
ent->s.pos.trTime = level.time;
|
|
|
|
VectorCopy(groundSpot, ent->s.pos.trBase);
|
|
VectorCopy(groundSpot, ent->r.currentOrigin);
|
|
|
|
if (ent->s.apos.trType != TR_STATIONARY)
|
|
{
|
|
ent->s.apos.trType = TR_STATIONARY;
|
|
ent->s.apos.trTime = level.time;
|
|
|
|
ent->s.apos.trBase[ROLL] = 0;
|
|
ent->s.apos.trBase[PITCH] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// check think function after bouncing
|
|
G_RunThink( ent );
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
|
|
|
|
|
|
|