Esempio n. 1
0
/*
================
CG_ReflectVelocity
================
*/
void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) {
	vec3_t	velocity;
	float	dot;
	int		hitTime;

	// reflect the velocity on the trace plane
	hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction;
	BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity );
	dot = DotProduct( velocity, trace->plane.normal );
	VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta );

	VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta );

	VectorCopy( trace->endpos, le->pos.trBase );
	le->pos.trTime = cg.time;

	// check for stop, making sure that even on low FPS systems it doesn't bobble
	if ( trace->allsolid || 
		( trace->plane.normal[2] > 0 && 
		( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) {
		le->pos.trType = TR_STATIONARY;
	} else {

	}
}
Esempio n. 2
0
/*
================
CG_ReflectVelocity
================
*/
void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) {
	bvec3_t	velocity;
	bfixed	dot;
	int		hitTime;

	// reflect the velocity on the trace plane
	hitTime = cg.time - cg.frametime + FIXED_TO_INT(MAKE_GFIXED(cg.frametime) * trace->fraction);
	BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity );
	dot = FIXED_VEC3DOT( velocity, trace->plane.normal );
	FIXED_VEC3MA_R( velocity, -BFIXED(2,0)*dot, trace->plane.normal, le->pos.trDelta );

	FIXED_VEC3SCALE( le->pos.trDelta, le->bounceFactor, le->pos.trDelta );

	VectorCopy( trace->endpos, le->pos.trBase );
	le->pos.trTime = cg.time;


	// check for stop, making sure that even on low FPS systems it doesn't bobble
	if ( trace->allsolid || 
		( trace->plane.normal[2] > AFIXED_0 && 
		( le->pos.trDelta[2] < BFIXED(40,0) || le->pos.trDelta[2] < -MAKE_BFIXED(cg.frametime) * le->pos.trDelta[2] ) ) ) {
		le->pos.trType = TR_STATIONARY;
	} else {

	}
}
Esempio n. 3
0
/*
================
G_PredictBounceMissile

================
*/
void G_PredictBounceMissile( gentity_t *ent, trajectory_t *pos, trace_t *trace, int time ) {
	vec3_t velocity, origin;
	float dot;
	int hitTime;

	BG_EvaluateTrajectory( pos, time, origin );

	// reflect the velocity on the trace plane
	hitTime = time;
	BG_EvaluateTrajectoryDelta( pos, hitTime, velocity );
	dot = DotProduct( velocity, trace->plane.normal );
	VectorMA( velocity, -2 * dot, trace->plane.normal, pos->trDelta );

	if ( ent->s.eFlags & EF_BOUNCE_HALF ) {
		if ( ent->s.eFlags & EF_BOUNCE ) {     // both flags marked, do a third type of bounce
			VectorScale( pos->trDelta, 0.35, pos->trDelta );
		} else {
			VectorScale( pos->trDelta, 0.65, pos->trDelta );
		}

		// check for stop
		if ( trace->plane.normal[2] > 0.2 && VectorLength( pos->trDelta ) < 40 ) {
			VectorCopy( trace->endpos, pos->trBase );
			return;
		}
	}

	VectorAdd( origin, trace->plane.normal, pos->trBase );
	pos->trTime = time;
}
Esempio n. 4
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_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;
    }
  }

  VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin );
  VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
  ent->s.pos.trTime = level.time;
}
Esempio n. 5
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_HALF ) {
#ifndef SMOKINGUNS
		VectorScale( ent->s.pos.trDelta, 0.65, ent->s.pos.trDelta );
#else
		VectorScale( ent->s.pos.trDelta, 0.17, ent->s.pos.trDelta );
#endif
		// check for stop
		if ( trace->plane.normal[2] > 0.2 && VectorLength( ent->s.pos.trDelta ) < 40 ) {
			G_SetOrigin( ent, trace->endpos );

#ifdef SMOKINGUNS
			if(!Q_stricmp(ent->classname, "grenadeno")){
				ent->classname = "grenadeend";
				VectorCopy(trace->endpos, ent->s.origin2);
			}
#endif
			return;
		}
	}

	VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
	ent->s.pos.trTime = level.time;
}
Esempio n. 6
0
/*
================
G_BounceMissile

================
*/
void G_BounceMissile( gentity_t *ent, trace_t *trace )
{
    vec3_t  velocity;
    float dot;
    int   hitTime;
    float cor = 1.;

    if( !strcmp( ent->classname, "bounceball" ) )
        cor = .8;

    // 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, -(1+cor) * dot, trace->plane.normal, ent->s.pos.trDelta );

    if( ent->s.eFlags & EF_BOUNCE_HALF )
    {
        if(!strcmp(ent->classname,"grenade") && g_proximityMines.integer) {
            VectorScale( ent->s.pos.trDelta, 0.00, ent->s.pos.trDelta );
        } else {
            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;
        }
    }

    VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin );
    VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
    ent->s.pos.trTime = level.time;
}
Esempio n. 7
0
void G_BounceMissile_Q3( gentity_t *ent, trace_t *trace ) {
	vector3	velocity;
	float	dot;
	int		hitTime;

	// reflect the velocity on the trace plane
	hitTime = level.previousTime + (int)(( 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->flags & FL_BOUNCE_HALF ) {
		VectorScale( &ent->s.pos.trDelta, 0.65f, &ent->s.pos.trDelta );
		// check for stop
		if ( trace->plane.normal.z > 0.2f && VectorLength( &ent->s.pos.trDelta ) < 40 ) {
			G_SetOrigin( ent, &trace->endpos );
			ent->s.time = level.time / 4;
			return;
		}
	}

	VectorAdd( &ent->r.currentOrigin, &trace->plane.normal, &ent->r.currentOrigin);
	VectorCopy( &ent->r.currentOrigin, &ent->s.pos.trBase );
	ent->s.pos.trTime = level.time;
}
Esempio n. 8
0
/*
================
G_BounceItem

================
*/
void G_BounceItem( 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 );

	// cut the velocity to keep from bouncing forever
	VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );

	// check for stop
	if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
		trace->endpos[2] += 1.0;	// make sure it is off ground
		SnapVector( trace->endpos );
		G_SetOrigin( ent, trace->endpos );
		ent->s.groundEntityNum = trace->entityNum;
		return;
	}

	VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
	ent->s.pos.trTime = level.time;
}
Esempio n. 9
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->flags & FL_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->flags & FL_BOUNCE_HALF )
    {
        VectorScale( ent->s.pos.trDelta, 0.65f, 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--;
    }
}
Esempio n. 10
0
/*
================
G_BounceMissile

================
*/
void G_BounceMissile( gentity_t *ent, trace_t *trace ) {
	vec3_t velocity;
	float dot;
	int hitTime;

// Arnout: removed this for MP as well (was already gone from SP)
/*
		// Ridah, if we are a grenade, and we have hit an AI that is waiting to catch us, give them a grenade, and delete ourselves
	if ((ent->splashMethodOfDeath == MOD_GRENADE_SPLASH) && (g_entities[trace->entityNum].flags & FL_AI_GRENADE_KICK) &&
		(trace->endpos[2] > g_entities[trace->entityNum].r.currentOrigin[2])) {
		g_entities[trace->entityNum].grenadeExplodeTime = ent->nextthink;
		g_entities[trace->entityNum].flags &= ~FL_AI_GRENADE_KICK;
		Add_Ammo( &g_entities[trace->entityNum], WP_GRENADE_LAUNCHER, 1, qfalse );	//----(SA)	modified
		G_FreeEntity( ent );
		return;
	}
*/
	// 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 );

	// RF, record this for mover pushing
	if ( trace->plane.normal[2] > 0.2 /*&& VectorLength( ent->s.pos.trDelta ) < 40*/ ) {
		ent->s.groundEntityNum = trace->entityNum;
	}

	if ( ent->s.eFlags & EF_BOUNCE_HALF ) {
		if ( ent->s.eFlags & EF_BOUNCE ) {     // both flags marked, do a third type of bounce
			VectorScale( ent->s.pos.trDelta, 0.35, ent->s.pos.trDelta );
		} else {
			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 ) {
//----(SA)	make the world the owner of the dynamite, so the player can shoot it after it stops moving
			if ( ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_DYNAMITE2 ) {
				ent->r.ownerNum = ENTITYNUM_WORLD;
			}
//----(SA)	end
			G_SetOrigin( ent, trace->endpos );
			return;
		}
	}

	SnapVector( ent->s.pos.trDelta );

	VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin );
	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );

	SnapVector( ent->s.pos.trBase );
	ent->s.pos.trTime = level.time;
}
Esempio n. 11
0
/*
================
G_Bounce

================
*/
static void G_Bounce( gentity_t *ent, trace_t *trace )
{
	vec3_t   velocity;
	float    dot;
	int      hitTime;
	float    minNormal;
	qboolean invert = qfalse;

	// 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.eType == ET_BUILDABLE )
	{
		minNormal = BG_Buildable( ent->s.modelindex )->minNormal;
		invert = BG_Buildable( ent->s.modelindex )->invertNormal;
	}
	else
	{
		minNormal = 0.707f;
	}

	// cut the velocity to keep from bouncing forever
	if ( ( trace->plane.normal[ 2 ] >= minNormal ||
	       ( invert && trace->plane.normal[ 2 ] <= -minNormal ) ) &&
	     trace->entityNum == ENTITYNUM_WORLD )
	{
		VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );
	}
	else
	{
		VectorScale( ent->s.pos.trDelta, 0.3f, ent->s.pos.trDelta );
	}

	if ( VectorLength( ent->s.pos.trDelta ) < 10 )
	{
		VectorMA( trace->endpos, 0.5f, trace->plane.normal, trace->endpos );  // make sure it is off ground
		G_SetOrigin( ent, trace->endpos );
		ent->s.groundEntityNum = trace->entityNum;
		VectorCopy( trace->plane.normal, ent->s.origin2 );
		VectorSet( ent->s.pos.trDelta, 0.0f, 0.0f, 0.0f );
		return;
	}

	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
	VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin );
	ent->s.pos.trTime = level.time;
}
Esempio n. 12
0
/*
================
G_BounceObject

================
*/
void G_BounceObject( gentity_t *ent, trace_t *trace ) 
{
	vec3_t	velocity;
	float	dot, bounceFactor;
	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 );
//	bounceFactor = 60/ent->mass;		// NOTENOTE Mass is not yet implemented
	bounceFactor = 1.0f;
	if ( bounceFactor > 1.0f )
	{
		bounceFactor = 1.0f;
	}
	VectorMA( velocity, -2*dot*bounceFactor, trace->plane.normal, ent->s.pos.trDelta );

	//FIXME: customized or material-based impact/bounce sounds
	if ( ent->flags & FL_BOUNCE_HALF ) 
	{
		VectorScale( ent->s.pos.trDelta, 0.5, ent->s.pos.trDelta );

		// check for stop
		if ( ((trace->plane.normal[2] > 0.7&&g_gravity.value>0) || (trace->plane.normal[2]<-0.7&&g_gravity.value<0)) && ((ent->s.pos.trDelta[2]<40&&g_gravity.value>0)||(ent->s.pos.trDelta[2]>-40&&g_gravity.value<0)) ) //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 + 500;
			ent->s.apos.trType = TR_STATIONARY;
			VectorCopy( ent->r.currentAngles, ent->s.apos.trBase );
			VectorCopy( trace->endpos, ent->r.currentOrigin );
			VectorCopy( trace->endpos, ent->s.pos.trBase );
			ent->s.pos.trTime = level.time;
			return;
		}
	}

	// NEW--It would seem that we want to set our trBase to the trace endpos
	//	and set the trTime to the actual time of impact....
	//	FIXME: Should we still consider adding the normal though??
	VectorCopy( trace->endpos, ent->r.currentOrigin );
	ent->s.pos.trTime = hitTime;

	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
	VectorCopy( trace->plane.normal, ent->pos1 );//???
}
Esempio n. 13
0
/*
================
CG_ReflectVelocity
================
*/
void CG_ReflectVelocity( localEntity_t *le, trace_t *trace ) {
	vec3_t	velocity;
	float	dot;
	int		hitTime;

	// reflect the velocity on the trace plane
	hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction;
	BG_EvaluateTrajectoryDelta( &le->pos, hitTime, velocity );
	dot = DotProduct( velocity, trace->plane.normal );
	VectorMA( velocity, -2*dot, trace->plane.normal, le->pos.trDelta );

	VectorScale( le->pos.trDelta, le->bounceFactor, le->pos.trDelta );

	VectorCopy( trace->endpos, le->pos.trBase );
	le->pos.trTime = cg.time;


	// check for stop, making sure that even on low FPS systems it doesn't bobble

	if (le->leMarkType == LEMT_BLOOD && trace->startsolid)
		{
			//centity_t *cent;
			//cent = &cg_entities[trace->entityNum];
			//if (cent && cent->currentState.apos.trType != TR_STATIONARY)
			//	le->pos.trType = TR_STATIONARY;
		}
	else if ( trace->allsolid || 
		( trace->plane.normal[2] > 0 && 
		( le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2] ) ) ) {

//----(SA)	if it's a fragment and it's not resting on the world...
//			if(le->leType == LE_DEBRIS && trace->entityNum < (MAX_ENTITIES - 1))
			if(le->leType == LE_FRAGMENT && trace->entityNum < (MAX_ENTITIES - 1))
			{
				le->pos.trType = TR_GRAVITY_PAUSED;
			}
			else
			{
				le->pos.trType = TR_STATIONARY;
			}
	}
	else {

	}
}
Esempio n. 14
0
void G_BounceMissile_JA( gentity_t *ent, trace_t *trace ) {
	vector3	velocity;
	float	dot;
	int		hitTime;

	// reflect the velocity on the trace plane
	hitTime = level.previousTime + (int)(( 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->flags & FL_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.z > 0.7f && ent->s.pos.trDelta.z < 40 ) //this can happen even on very slightly sloped walls, so changed it from > 0 to > 0.7f
		{
			G_SetOrigin( ent, &trace->endpos );
			ent->nextthink = level.time + 100;
			return;
		}
	}
	else if ( ent->flags & FL_BOUNCE_HALF ) 
	{
		VectorScale( &ent->s.pos.trDelta, 0.65f, &ent->s.pos.trDelta );
		// check for stop
		if ( trace->plane.normal.z > 0.2f && VectorLength( &ent->s.pos.trDelta ) < 40 ) 
		{
			G_SetOrigin( ent, &trace->endpos );
			return;
		}
	}

	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--;
}
Esempio n. 15
0
/*
================
CG_ReflectVelocity
================
*/
void CG_ReflectVelocity(localEntity_t * le, trace_t * trace)
{
	vec3_t          velocity;
	float           dot;
	int             hitTime;

	// reflect the velocity on the trace plane
	hitTime = cg.time - cg.frametime + cg.frametime * trace->fraction;
	BG_EvaluateTrajectoryDelta(&le->pos, hitTime, velocity);
	dot = DotProduct(velocity, trace->plane.normal);
	VectorMA(velocity, -2 * dot, trace->plane.normal, le->pos.trDelta);

	VectorScale(le->pos.trDelta, le->bounceFactor, le->pos.trDelta);

	VectorCopy(trace->endpos, le->pos.trBase);
	le->pos.trTime = cg.time;


	// check for stop, making sure that even on low FPS systems it doesn't bobble
	if(trace->allsolid ||
	   (trace->plane.normal[2] > 0 && (le->pos.trDelta[2] < 40 || le->pos.trDelta[2] < -cg.frametime * le->pos.trDelta[2])))
	{
		le->pos.trType = TR_STATIONARY;
	}
	else
	{
		if(le->leFlags & LEF_TUMBLE)
		{
			// collided with a surface so calculate the new rotation axis
			CrossProduct(trace->plane.normal, velocity, le->rotAxis);
			le->angVel = VectorNormalize(le->rotAxis) / le->radius;

			// save current orientation as a rotation from model's base orientation
			QuatMultiply0(le->quatRot, le->quatOrient);

			// reset the orientation
			QuatClear(le->quatOrient);
		}
	}
}
Esempio n. 16
0
/*
================
G_BounceItem

================
*/
void G_BounceItem( 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, qfalse, ent->s.effect2Time );
	dot = DotProduct( velocity, trace->plane.normal );
	VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta );

	// cut the velocity to keep from bouncing forever
	VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta );

	// check for stop
	if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {
		if (g_flushItems.integer) {
			vectoangles( trace->plane.normal, ent->s.angles );
			ent->s.angles[0] += 90;
			if (ent->s.angles[0] > 0.0 && ent->s.angles[0] < 50.0) {
				// avoid freaky medpacks
				G_SetAngle( ent, ent->s.angles);
				trace->endpos[2] -= (tan(DEG2RAD(ent->s.angles[0])) * ITEM_RADIUS);
			} else {
				trace->endpos[2] += 1.0;   // make sure it is off ground
			}
		} else {
			trace->endpos[2] += 1.0;   // make sure it is off ground
		}

		SnapVector( trace->endpos );
		G_SetOrigin( ent, trace->endpos );
		ent->s.groundEntityNum = trace->entityNum;
		return;
	}

	VectorAdd( ent->r.currentOrigin, trace->plane.normal, ent->r.currentOrigin);
	VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase );
	ent->s.pos.trTime = level.time;
}
Esempio n. 17
0
/*
================
CG_AddFragment
================
*/
void CG_AddFragment( localEntity_t *le ) {
	vec3_t	newOrigin;
	trace_t	trace;

	if ( le->pos.trType == TR_STATIONARY ) {
		// sink into the ground if near the removal time
		int		t;
		float	oldZ;
		
		t = le->endTime - cg.time;
		if ( t < SINK_TIME ) {
			// we must use an explicit lighting origin, otherwise the
			// lighting would be lost as soon as the origin went
			// into the ground
			VectorCopy( le->refEntity.origin, le->refEntity.lightingOrigin );
			le->refEntity.renderfx |= RF_LIGHTING_ORIGIN;
			oldZ = le->refEntity.origin[2];
			le->refEntity.origin[2] -= 16 * ( 1.0 - (float)t / SINK_TIME );
			trap_R_AddRefEntityToScene( &le->refEntity );
			le->refEntity.origin[2] = oldZ;
		} else {
			trap_R_AddRefEntityToScene( &le->refEntity );
		}

		return;
	}

	// calculate new position
	BG_EvaluateTrajectory( &le->pos, cg.time, newOrigin );

	// trace a line from previous position to new position
	CG_Trace( &trace, le->refEntity.origin, NULL, NULL, newOrigin, -1, CONTENTS_SOLID );
	if ( trace.fraction == 1.0 ) {
		qboolean	inWater;
		vec3_t		newDelta;

		// still in free fall
		VectorCopy( newOrigin, le->refEntity.origin );

		// check for water
		inWater = CG_PointContents( newOrigin, -1 ) & CONTENTS_WATER;

		if ( inWater && le->pos.trType == TR_GRAVITY ) {
			BG_EvaluateTrajectoryDelta( &le->pos, cg.time, newDelta );
			le->pos.trType = TR_WATER_GRAVITY;
			VectorCopy( newOrigin, le->pos.trBase );
			VectorCopy( newDelta, le->pos.trDelta );
			le->pos.trTime = cg.time;
		}
		if ( !inWater && le->pos.trType == TR_WATER_GRAVITY ) {
			BG_EvaluateTrajectoryDelta( &le->pos, cg.time, newDelta );
			le->pos.trType = TR_GRAVITY;
			VectorCopy( newOrigin, le->pos.trBase );
			VectorCopy( newDelta, le->pos.trDelta );
			le->pos.trTime = cg.time;
		}

		if ( le->leFlags & LEF_TUMBLE ) {
			vec3_t angles;

			BG_EvaluateTrajectory( &le->angles, cg.time, angles );
			AnglesToAxis( angles, le->refEntity.axis );
		}

		trap_R_AddRefEntityToScene( &le->refEntity );

		// add a fire trail
		// CG_FireTrail( le );

		return;
	}

	// if it is in a nodrop zone, remove it
	// this keeps gibs from waiting at the bottom of pits of death
	// and floating levels
	if ( trap_CM_PointContents( trace.endpos, 0 ) & CONTENTS_NODROP ) {
		CG_FreeLocalEntity( le );
		return;
	}

	// stop rotation
	le->angles.trType = TR_STATIONARY;

	// leave a mark
	CG_FragmentBounceMark( le, &trace );

	// reflect the velocity on the trace plane
	CG_ReflectVelocity( le, &trace );

	trap_R_AddRefEntityToScene( &le->refEntity );
}
Esempio n. 18
0
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
#else
void G_MissileImpact( gentity_t *ent, trace_t *trace, int shaderNum ) {
#endif
	gentity_t		*other;
	qboolean		hitClient = qfalse;
#ifndef SMOKINGUNS
#ifdef MISSIONPACK
	vec3_t			forward, impactpoint, bouncedir;
	int				eFlags;
#endif
#else
	qboolean		hitKnife  = qfalse;
	vec3_t			bottledirs[ALC_COUNT];
#endif
	other = &g_entities[trace->entityNum];

#ifndef SMOKINGUNS
	// check for bounce
	if ( !other->takedamage &&
		( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
		G_BounceMissile( ent, trace );
		G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
		return;
	}

#ifdef MISSIONPACK
	if ( other->takedamage ) {
		if ( ent->s.weapon != WP_PROX_LAUNCHER ) {
			if ( other->client && other->client->invulnerabilityTime > level.time ) {
				//
				VectorCopy( ent->s.pos.trDelta, forward );
				VectorNormalize( forward );
				if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) {
					VectorCopy( bouncedir, trace->plane.normal );
					eFlags = ent->s.eFlags & EF_BOUNCE_HALF;
					ent->s.eFlags &= ~EF_BOUNCE_HALF;
					G_BounceMissile( ent, trace );
					ent->s.eFlags |= eFlags;
				}
				ent->target_ent = other;
				return;
			}
		}
	}
#endif
	// impact damage
	if (other->takedamage) {
#else
	if(other->takedamage)
		hitKnife = qtrue;

	// check for bounce
	if ( ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
		G_BounceMissile( ent, trace );
		return;
	}

	if (other->takedamage && ent->s.weapon != WP_DYNAMITE) {
#endif
		// 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
			}
#ifndef SMOKINGUNS
			G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
				ent->s.origin, ent->damage,
				0, ent->methodOfDeath);
#else

			// you can't make dynamite exploding by using a knife
			if(!(ent->s.weapon == WP_KNIFE && other->s.weapon == WP_DYNAMITE &&
				other->s.eType == ET_ITEM)){

				// prepare breakable, if not already initialized
				if(!(other->flags & FL_BREAKABLE_INIT))
					G_BreakablePrepare(other, shaderNum);

				G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
					ent->s.origin, ent->damage,
					0, ent->methodOfDeath);
			}
#endif
		}
	}

#ifndef SMOKINGUNS
	if( ent->s.weapon == WP_PROX_LAUNCHER ) {
		if( ent->s.pos.trType != TR_GRAVITY ) {
			return;
		}

		// if it's a player, stick it on to them (flag them and remove this entity)
		if( other->s.eType == ET_PLAYER && other->health > 0 ) {
			ProximityMine_Player( ent, other );
			return;
		}

		SnapVectorTowards( trace->endpos, ent->s.pos.trBase );
		G_SetOrigin( ent, trace->endpos );
		ent->s.pos.trType = TR_STATIONARY;
		VectorClear( ent->s.pos.trDelta );

		G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags );

		ent->think = ProximityMine_Activate;
		ent->nextthink = level.time + 2000;

		vectoangles( trace->plane.normal, ent->s.angles );
		ent->s.angles[0] += 90;

		// link the prox mine to the other entity
		ent->enemy = other;
		ent->die = ProximityMine_Die;
		VectorCopy(trace->plane.normal, ent->movedir);
		VectorSet(ent->r.mins, -4, -4, -4);
		VectorSet(ent->r.maxs, 4, 4, 4);
		trap_LinkEntity(ent);

		return;
	}

	if (!strcmp(ent->classname, "hook")) {
		gentity_t *nent;
		vec3_t v;

		nent = G_Spawn();
		if ( other->takedamage && other->client ) {

			G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
			nent->s.otherEntityNum = other->s.number;

			ent->enemy = other;

			v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5;
			v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5;
			v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5;

			SnapVectorTowards( v, ent->s.pos.trBase );	// save net bandwidth
		} else {
			VectorCopy(trace->endpos, v);
			G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
			ent->enemy = NULL;
		}

		SnapVectorTowards( v, ent->s.pos.trBase );	// save net bandwidth

		nent->freeAfterEvent = qtrue;
		// change over to a normal entity right at the point of impact
		nent->s.eType = ET_GENERAL;
		ent->s.eType = ET_GRAPPLE;

		G_SetOrigin( ent, v );
		G_SetOrigin( nent, v );

		ent->think = Weapon_HookThink;
		ent->nextthink = level.time + FRAMETIME;

		ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL;
		VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);

		trap_LinkEntity( ent );
		trap_LinkEntity( nent );

		return;
	}
#endif

	// is it cheaper in bandwidth to just remove this ent and create a new
	// one, rather than changing the missile into the explosion?

#ifndef SMOKINGUNS
	if ( other->takedamage && other->client ) {
#else
	// alcoohol impact
	if( !Q_stricmp(ent->classname, "alcohol")){
		// no event
		//G_AddEvent( ent, EV_MISSILE_ALCOHOL, DirToByte( trace->plane.normal));
	} else if( !Q_stricmp(ent->classname, "molotov")){
		// we have to launch the whiskey drops
		int i;

		// set the directions
		for(i = 0; i < ALC_COUNT; i++){
			int temp;

			VectorSet(bottledirs[i], (rand()%10)-5, (rand()%10)-5, (rand()%10)-3);

			// direction has to be exactly the same (client and server)
			temp = DirToByte(bottledirs[i]);
			ByteToDir(temp, bottledirs[i]);
		}

		// dirs
		BG_DirsToEntityState(&ent->s, bottledirs);

		// burning
		if(ent->s.apos.trDelta[0])
			G_AddEvent( ent, EV_MISSILE_FIRE, DirToByte( trace->plane.normal));
		// not burning
		else
			G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal));
	} else if ( other->takedamage && other->client ) {
#endif
		G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
		ent->s.otherEntityNum = other->s.number;
#ifndef SMOKINGUNS
	} else if( trace->surfaceFlags & SURF_METALSTEPS ) {
#else
	} else if( trace->surfaceFlags & SURF_METAL ) {
#endif
		G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) );
	} else {
		G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
	}

#ifndef SMOKINGUNS
	ent->freeAfterEvent = qtrue;

	// change over to a normal entity right at the point of impact
	ent->s.eType = ET_GENERAL;
#else
	if(Q_stricmp(ent->classname, "knife")){
		ent->freeAfterEvent = qtrue;
		// change over to a normal entity right at the point of impact
		ent->s.eType = ET_GENERAL;
	} else {
		vec3_t dir;
		gitem_t			*item;

		item = BG_FindItemForWeapon(WP_KNIFE);

		ent->s.modelindex = item-bg_itemlist;
		ent->s.modelindex2 = 1;

		ent->item = item;

		ent->s.eType = ET_ITEM;
		ent->s.pos.trType = TR_GRAVITY;
		ent->physicsBounce = 0.01f;
		ent->r.contents = CONTENTS_TRIGGER;

		ent->touch = Touch_Item;
		ent->nextthink = level.time + 100;
		ent->think = G_KnifeThink;
		ent->wait = level.time + 30000;
		ent->flags |= FL_THROWN_ITEM;

		vectoangles(ent->s.pos.trDelta, dir);

		VectorCopy(dir, ent->s.apos.trBase);
		VectorCopy(dir, ent->r.currentAngles);
	}
	//modified by Spoon END
#endif

	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++;
			}
		}
	}

	// spawn alcohol missiles
#ifdef SMOKINGUNS
	if(!Q_stricmp(ent->classname, "molotov")){
		BottleBreak( ent, trace->endpos, trace->plane.normal, bottledirs);
	}
#endif

	trap_LinkEntity( ent );
}

/*
================
G_RunMissile
================
*/
void G_RunMissile( gentity_t *ent ) {
	vec3_t		origin;
	trace_t		tr;
	int			passent;
#ifdef SMOKINGUNS
	int			shaderNum;
	gentity_t	*traceEnt;
#endif

	// 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;
	}
#ifndef SMOKINGUNS
	// prox mines that left the owner bbox will attach to anything, even the owner
	else if (ent->s.weapon == WP_PROX_LAUNCHER && ent->count) {
		passent = ENTITYNUM_NONE;
	}
#endif
	else {
		// ignore interactions with the missile owner
		passent = ent->r.ownerNum;
	}
	// trace a line from the previous position to the current position
#ifndef SMOKINGUNS
	trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
#else
	shaderNum = trap_Trace_New2( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask );
	traceEnt = &g_entities[tr.entityNum];
#endif

	if ( tr.startsolid || tr.allsolid ) {
		// make sure the tr.entityNum is set to the entity we're stuck in
#ifndef SMOKINGUNS
		trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask );
#else
		trap_Trace_New( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask );
#endif
		tr.fraction = 0;
	}
	else {
		VectorCopy( tr.endpos, ent->r.currentOrigin );
	}

	trap_LinkEntity( ent );

	if ( tr.fraction != 1 ) {

#ifdef SMOKINGUNS
		VectorCopy(origin, ent->s.origin2);
#endif

		// 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 its a dynamite or molotov let it move 10 seconds before deleting it
#ifdef SMOKINGUNS
			if(ent->s.weapon == WP_DYNAMITE || ent->s.weapon == WP_MOLOTOV
				|| ent->s.weapon == WP_KNIFE){

				if(ent->mappart >= level.time && ent->mappart){
					goto think;
				} else if(ent->mappart){
					ent->mappart = 0;
				} else {
					ent->mappart = level.time + 5000;
					goto think;
				}
			}
#endif
			G_FreeEntity( ent );
			return;
		}
#ifndef SMOKINGUNS
		G_MissileImpact( ent, &tr );
		if ( ent->s.eType != ET_MISSILE ) {
#else
		G_MissileImpact( ent, &tr, shaderNum );
		if ( ent->s.eType != ET_MISSILE && ent->s.eType != ET_ITEM) {
#endif
			return;		// exploded
		}
	}
#ifndef SMOKINGUNS
	// if the prox mine wasn't yet outside the player body
	if (ent->s.weapon == WP_PROX_LAUNCHER && !ent->count) {
		// check if the prox mine is outside the owner bbox
		trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask );
		if (!tr.startsolid || tr.entityNum != ent->r.ownerNum) {
			ent->count = 1;
		}
	}
#endif

think:
	// check think function after bouncing
	G_RunThink( ent );
}


//=============================================================================

#ifndef SMOKINGUNS
/*
=================
fire_plasma

=================
*/
gentity_t *fire_plasma (gentity_t *self, vec3_t start, vec3_t dir) {
	gentity_t	*bolt;

	VectorNormalize (dir);

	bolt = G_Spawn();
	bolt->classname = "plasma";
	bolt->nextthink = level.time + 10000;
	bolt->think = G_ExplodeMissile;
	bolt->s.eType = ET_MISSILE;
	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	bolt->s.weapon = WP_PLASMAGUN;
	bolt->r.ownerNum = self->s.number;
	bolt->parent = self;
	bolt->damage = 20;
	bolt->splashDamage = 15;
	bolt->splashRadius = 20;
	bolt->methodOfDeath = MOD_PLASMA;
	bolt->splashMethodOfDeath = MOD_PLASMA_SPLASH;
	bolt->clipmask = MASK_SHOT;
	bolt->target_ent = NULL;

	bolt->s.pos.trType = TR_LINEAR;
	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;		// move a bit on the very first frame
	VectorCopy( start, bolt->s.pos.trBase );
	VectorScale( dir, 2000, bolt->s.pos.trDelta );
	SnapVector( bolt->s.pos.trDelta );			// save net bandwidth

	VectorCopy (start, bolt->r.currentOrigin);

	return bolt;
}

//=============================================================================


/*
=================
fire_grenade
=================
*/
gentity_t *fire_grenade (gentity_t *self, vec3_t start, vec3_t dir) {
	gentity_t	*bolt;

	VectorNormalize (dir);

	bolt = G_Spawn();
	bolt->classname = "grenade";
	bolt->nextthink = level.time + 2500;
	bolt->think = G_ExplodeMissile;
	bolt->s.eType = ET_MISSILE;
	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;
	bolt->s.weapon = WP_GRENADE_LAUNCHER;
	bolt->s.eFlags = EF_BOUNCE_HALF;
	bolt->r.ownerNum = self->s.number;
	bolt->parent = self;
	bolt->damage = 100;
	bolt->splashDamage = 100;
	bolt->splashRadius = 150;
	bolt->methodOfDeath = MOD_GRENADE;
	bolt->splashMethodOfDeath = MOD_GRENADE_SPLASH;
	bolt->clipmask = MASK_SHOT;
	bolt->target_ent = NULL;

	bolt->s.pos.trType = TR_GRAVITY;
	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;		// move a bit on the very first frame
	VectorCopy( start, bolt->s.pos.trBase );
	VectorScale( dir, 700, bolt->s.pos.trDelta );
	SnapVector( bolt->s.pos.trDelta );			// save net bandwidth

	VectorCopy (start, bolt->r.currentOrigin);

	return bolt;
}
Esempio n. 19
0
static void MissileImpact( gentity_t *ent, trace_t *trace )
{
	int       dirAsByte, impactFlags;
	const missileAttributes_t *ma = BG_Missile( ent->s.modelindex );
	gentity_t *hitEnt   = &g_entities[ trace->entityNum ];
	gentity_t *attacker = &g_entities[ ent->r.ownerNum ];

	// Returns whether damage and hit effects should be done and played.
	std::function<int(gentity_t*, trace_t*, gentity_t*)> impactFunc;

	// Check for bounce.
	if ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) &&
	     !HasComponents<HealthComponent>(*hitEnt->entity) )
	{
		BounceMissile( ent, trace );

		if ( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
		{
			G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
		}

		return;
	}

	// Call missile specific impact functions.
	switch( ent->s.modelindex )
	{
		case MIS_GRENADE:      impactFunc = ImpactGrenade;     break;
		case MIS_FIREBOMB:     impactFunc = ImpactGrenade;     break;
		case MIS_FLAMER:       impactFunc = ImpactFlamer;      break;
		case MIS_FIREBOMB_SUB: impactFunc = ImpactFirebombSub; break;
		case MIS_LOCKBLOB:     impactFunc = ImpactLockblock;   break;
		case MIS_SLOWBLOB:     impactFunc = ImpactSlowblob;    break;
		case MIS_HIVE:         impactFunc = ImpactHive;        break;
		default:               impactFunc = DefaultImpactFunc; break;
	}

	impactFlags = impactFunc( ent, trace, hitEnt );

	// Deal impact damage.
	if ( !( impactFlags & MIF_NO_DAMAGE ) )
	{
		if ( ent->damage && G_Alive( hitEnt ) )
		{
			vec3_t dir;

			BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, dir );

			if ( VectorNormalize( dir ) == 0 )
			{
				dir[ 2 ] = 1; // stepped on a grenade
			}

			int dflags = 0;
			if ( !ma->doLocationalDamage ) dflags |= DAMAGE_NO_LOCDAMAGE;
			if ( ma->doKnockback )         dflags |= DAMAGE_KNOCKBACK;

			hitEnt->entity->Damage(ent->damage * MissileTimeDmgMod(ent), attacker,
			                       Vec3::Load(trace->endpos), Vec3::Load(dir), dflags,
			                       (meansOfDeath_t)ent->methodOfDeath);
		}

		// splash damage (doesn't apply to person directly hit)
		if ( ent->splashDamage )
		{
			G_RadiusDamage( trace->endpos, ent->parent,
			                ent->splashDamage * MissileTimeSplashDmgMod( ent ),
			                ent->splashRadius, hitEnt, ( ma->doKnockback ? DAMAGE_KNOCKBACK : 0 ),
			                ent->splashMethodOfDeath );
		}
	}

	// Play hit effects and remove the missile.
	if ( !( impactFlags & MIF_NO_EFFECT ) )
	{
		// Use either the trajectory direction or the surface normal for the hit event.
		if ( ma->impactFlightDirection )
		{
			vec3_t trajDir;
			BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, trajDir );
			VectorNormalize( trajDir );
			dirAsByte = DirToByte( trajDir );
		}
		else
		{
			dirAsByte = DirToByte( trace->plane.normal );
		}

		// Add hit event.
		if ( HasComponents<HealthComponent>(*hitEnt->entity) )
		{
			G_AddEvent( ent, EV_MISSILE_HIT_ENTITY, dirAsByte );

			ent->s.otherEntityNum = hitEnt->s.number;
		}
		else if ( trace->surfaceFlags & SURF_METAL )
		{
			G_AddEvent( ent, EV_MISSILE_HIT_METAL, dirAsByte );
		}
		else
		{
			G_AddEvent( ent, EV_MISSILE_HIT_ENVIRONMENT, dirAsByte );
		}

		ent->freeAfterEvent = true;

		// HACK: Change over to a general entity at the point of impact.
		ent->s.eType = ET_GENERAL;

		// Prevent map models from appearing at impact point.
		ent->s.modelindex = 0;

		// Save net bandwith.
		G_SnapVectorTowards( trace->endpos, ent->s.pos.trBase );

		G_SetOrigin( ent, trace->endpos );

		trap_LinkEntity( ent );
	}
	// If no impact happened, check if we should continue or free ourselves.
	else if ( !( impactFlags & MIF_NO_FREE ) )
	{
		G_FreeEntity( ent );
	}
}
Esempio n. 20
0
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
    gentity_t		*other;
    qboolean		hitClient = qfalse;
    qboolean		isKnockedSaber = qfalse;

    other = &g_entities[trace->entityNum];

    // check for bounce
    if ( other->takedamage &&
            (ent->bounceCount > 0 || ent->bounceCount == -5) &&
            ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) &&
            (g_tweakWeapons.integer & WT_ROCKET_MORTAR && ent->s.weapon == WP_REPEATER && ent->bounceCount == 50 && ent->setTime && ent->setTime > level.time - 300))
    {   //if its a direct hit and first 500ms of mortar, bounce off player.
        G_BounceMissile( ent, trace );
        G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
        return;
    }
    else if ( !other->takedamage &&
              (ent->bounceCount > 0 || ent->bounceCount == -5) &&
              ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { //only on the first bounce vv
        if (!(g_tweakWeapons.integer & WT_ROCKET_MORTAR && ent->s.weapon == WP_REPEATER && ent->bounceCount == 50 && ent->setTime && ent->setTime < level.time - 1000))//give this mortar a 1 second 'fuse' until its armed
        {
            G_BounceMissile( ent, trace );
            G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
            return;
        }
    }
    else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF))
    {   //this is a knocked-away saber
        if (ent->bounceCount > 0 || ent->bounceCount == -5)
        {
            G_BounceMissile( ent, trace );
            G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
            return;
        }

        isKnockedSaber = qtrue;
    }

    // I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else
    if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_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->flags &= ~FL_BOUNCE_SHRAPNEL;
        }
        //trap->Print("Shrapnel is still there\n");
        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 );
    	}
    	//trap->linkentity( ent );
    	return;
    }
    */

    if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber)
    {   //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 (!isKnockedSaber)
    {
        if (other->takedamage && other->client && other->client->ps.duelInProgress &&
                other->client->ps.duelIndex != ent->r.ownerNum)
        {
            goto killProj;
        }
    }

    if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY)
    {
        if (ent->methodOfDeath != MOD_REPEATER_ALT &&
                ent->methodOfDeath != MOD_ROCKET &&
                ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
                ent->methodOfDeath != MOD_ROCKET_HOMING &&
                ent->methodOfDeath != MOD_THERMAL &&
                ent->methodOfDeath != MOD_THERMAL_SPLASH &&
                ent->methodOfDeath != MOD_TRIP_MINE_SPLASH &&
                ent->methodOfDeath != MOD_TIMED_MINE_SPLASH &&
                ent->methodOfDeath != MOD_DET_PACK_SPLASH &&
                ent->methodOfDeath != MOD_VEHICLE &&
                ent->methodOfDeath != MOD_CONC &&
                ent->methodOfDeath != MOD_CONC_ALT &&
                ent->methodOfDeath != MOD_SABER &&
                ent->methodOfDeath != MOD_TURBLAST)
        {
            vec3_t fwd;

            if (trace)
            {
                VectorCopy(trace->plane.normal, fwd);
            }
            else
            {   //oh well
                AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
            }

            G_DeflectMissile(other, ent, fwd);
            G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
            return;
        }
    }

    if ((other->flags & FL_SHIELDED) &&
            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->s.weapon != WP_EMPLACED_GUN &&
            ent->methodOfDeath != MOD_REPEATER_ALT &&
            ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
            ent->methodOfDeath != MOD_TURBLAST &&
            ent->methodOfDeath != MOD_VEHICLE &&
            ent->methodOfDeath != MOD_CONC &&
            ent->methodOfDeath != MOD_CONC_ALT &&
            !(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) )
    {
        vec3_t fwd;

        if (other->client)
        {
            AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL);
        }
        else
        {
            AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
        }

        G_DeflectMissile(other, ent, fwd);
        G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
        return;
    }

    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 &&
            ent->methodOfDeath != MOD_REPEATER_ALT &&
            ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
            ent->methodOfDeath != MOD_CONC &&
            ent->methodOfDeath != MOD_CONC_ALT &&
            other->client->ps.saberBlockTime < level.time &&
            !isKnockedSaber &&
            WP_SaberCanBlock(other, ent->r.currentOrigin, 0, 0, qtrue, 0)) //loda fixme, add check for dimensions for blocking here?
    {   //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_SABER_DEFENSE];

        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;
        te->s.weapon = 0;//saberNum
        te->s.legsAnim = 0;//bladeNum

        /*if (other->client->ps.velocity[2] > 0 ||
        	other->client->pers.cmd.forwardmove ||
        	other->client->pers.cmd.rightmove)
        	*/
        if (other->client->ps.velocity[2] > 0 ||
                other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge.
        {
            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;

        //For jedi AI
        other->client->ps.saberEventFlags |= SEF_DEFLECTED;

        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) && !isKnockedSaber)
    {   //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 &&
                ent->methodOfDeath != MOD_CONC &&
                (g_entities[ent->r.ownerNum].s.bolt1 == other->s.bolt1) &&//loda fixme, this stops missiles deflecting, but they still dont passthrough...
                ent->methodOfDeath != MOD_CONC_ALT /*&&
			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_SABER_DEFENSE];

            //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);
            if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0)
            {
                WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue);
            }

            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;
            te->s.weapon = 0;//saberNum
            te->s.legsAnim = 0;//bladeNum

            /*if (otherOwner->client->ps.velocity[2] > 0 ||
            	otherOwner->client->pers.cmd.forwardmove ||
            	otherOwner->client->pers.cmd.rightmove)*/
            if (otherOwner->client->ps.velocity[2] > 0 ||
                    otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge.
            {
                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;

            //For jedi AI
            otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED;

            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;
    }

//JAPRO - Serverside - Flag punting - Start
    if (g_allowFlagThrow.integer && !other->takedamage && other->s.eType == ET_ITEM)
    {
        vec3_t velocity;

        if (ent->s.weapon == WP_REPEATER && (ent->s.eFlags & EF_ALT_FIRING))
        {
            other->s.pos.trType = TR_GRAVITY;
            other->s.pos.trTime = level.time;
            BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
            VectorScale( velocity, 0.7f, other->s.pos.trDelta );
            VectorCopy( other->r.currentOrigin, other->s.pos.trBase );
        }
        else if (ent->s.weapon == WP_ROCKET_LAUNCHER && (ent->s.eFlags & EF_ALT_FIRING))
        {
            other->s.pos.trType = TR_GRAVITY;
            other->s.pos.trTime = level.time;
            BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
            VectorScale( velocity, 2.5f, other->s.pos.trDelta );
            VectorCopy( other->r.currentOrigin, other->s.pos.trBase );
        }
        else if (ent->s.weapon == WP_ROCKET_LAUNCHER)
        {
            other->s.pos.trType = TR_GRAVITY;
            other->s.pos.trTime = level.time;
            BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
            VectorScale( velocity, 0.9f, other->s.pos.trDelta );
            VectorCopy( other->r.currentOrigin, other->s.pos.trBase );
        }
        else if (ent->s.weapon == WP_THERMAL)
        {
            other->s.pos.trType = TR_GRAVITY;
            other->s.pos.trTime = level.time;
            BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
            VectorScale( velocity, 0.9f, other->s.pos.trDelta ); //tweak?
            VectorCopy( other->r.currentOrigin, other->s.pos.trBase );
        }
    }
//JAPRO - Serverside - Flag punting - End

    // impact damage
    if (other->takedamage && !isKnockedSaber) {
        // FIXME: wrong damage direction?
        if ( ent->damage ) {
            vec3_t	velocity;
            qboolean didDmg = qfalse;

            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
            }

            //damage falloff option, assumes bullet lifetime is 10,000 (default)
            if ((g_tweakWeapons.integer & WT_NO_SPREAD) &&
                    ((ent->s.weapon == WP_BLASTER && (ent->s.eFlags & EF_ALT_FIRING)) ||
                     (ent->s.weapon == WP_REPEATER && !(ent->s.eFlags & EF_ALT_FIRING))
                    ))
            {   //If the weapon has spread, just reduce damage based on distance for nospread tweak.  This should probably be accompanied with the damagenumber setting so you can keep track of your dmg..
                float lifetime = (10000 - ent->nextthink + level.time) * 0.001;
                //float scale = powf(2, -lifetime);
                float scale = -1.5 * lifetime + 1;

                scale += 0.1f; //offset it a bit so super close shots dont get affected at all

                if (scale < 0.2f)
                    scale = 0.2f;
                else if (scale > 1.0f)
                    scale = 1.0f;

                ent->damage *= scale;

                //trap->SendServerCommand(-1, va("chat \"Missile has been alive for %.2f s new dmg is %i scale is %.2f\n\"", lifetime, ent->damage, scale));
            }

            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))
                {
                    /* fix: there are rare situations where flechette did
                    explode by timeout AND by impact in the very same frame, then here
                    ent->think was set to G_FreeEntity, so the folowing think
                    did invalidate this entity, BUT it would be reused later in this
                    function for explosion event. This, then, would set ent->freeAfterEvent
                    to qtrue, so event later, when reusing this entity by using G_InitEntity(),
                    it would have this freeAfterEvent set AND this would in case of dropped
                    item erase it from game immeadiately. THIS for example caused
                    very rare flag dissappearing bug.	 */
                    if (ent->think == WP_flechette_alt_blow)
                        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);
                    didDmg = qtrue;
                }
            }
            else
            {
                G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
                          /*ent->s.origin*/ent->r.currentOrigin, ent->damage,
                          0, ent->methodOfDeath);
                didDmg = qtrue;
            }

            if (didDmg && other && other->client)
            {   //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever.
                class_t	npc_class = other->client->NPC_class;

                // If we are a robot and we aren't currently doing the full body electricity...
                if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE ||
                        npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE ||
                        npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd
                        npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY )
                {
                    // special droid only behaviors
                    if ( other->client->ps.electrifyTime < level.time + 100 )
                    {
                        // ... do the effect for a split second for some more feedback
                        other->client->ps.electrifyTime = level.time + 450;
                    }
                    //FIXME: throw some sparks off droids,too
                }
            }
        }

        if ( ent->s.weapon == WP_DEMP2 )
        {   //a hit with demp2 decloaks people, disables ships
            if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE )
            {   //hit a vehicle
                if ( other->m_pVehicle //valid vehicle ent
                        && other->m_pVehicle->m_pVehicleInfo//valid stats
                        && (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders
                            ||(other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp("vehicle_proj", ent->classname ) == 0) )//only vehicle ion weapons affect a fighter in this manner
                        && !FighterIsLanded( other->m_pVehicle , &other->client->ps )//not landed
                        && !(other->spawnflags&2) )//and not suspended
                {   //vehicles hit by "ion cannons" lose control
                    if ( other->client->ps.electrifyTime > level.time )
                    {   //add onto it
                        //FIXME: extern the length of the "out of control" time?
                        other->client->ps.electrifyTime += Q_irand(200,500);
                        if ( other->client->ps.electrifyTime > level.time + 4000 )
                        {   //cap it
                            other->client->ps.electrifyTime = level.time + 4000;
                        }
                    }
                    else
                    {   //start it
                        //FIXME: extern the length of the "out of control" time?
                        other->client->ps.electrifyTime = level.time + Q_irand(200,500);
                    }
                }
            }
            else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] )
            {
                Jedi_Decloak( other );
                if ( ent->methodOfDeath == MOD_DEMP2_ALT )
                {   //direct hit with alt disables cloak forever
                    //permanently disable the saboteur's cloak
                    other->client->cloakToggleTime = Q3_INFINITE;
                }
                else
                {   //temp disable
                    other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 );
                }
            }
        }
    }

#if _GRAPPLE//_GRAPPLE
    if (!strcmp(ent->classname, "laserTrap") && ent->s.weapon == WP_BRYAR_PISTOL) {
        //gentity_t *nent;
        vec3_t v;

        /*
        nent = G_Spawn(qtrue);
        nent->freeAfterEvent = qtrue;
        nent->s.weapon = WP_BRYAR_PISTOL;//WP_GRAPPLING_HOOK; -- idk what this is
        nent->s.saberInFlight = qtrue;
        nent->s.owner = ent->s.owner;
        */

        ent->enemy = NULL;
        ent->s.otherEntityNum = -1;
        ent->s.groundEntityNum = -1;

        if ( other->s.eType == ET_MOVER || (other->client && !( other->s.eFlags & EF_DEAD ) ) ) {
            if ( other->client ) {
                //G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );							//Event

                if (!ent->s.hasLookTarget) {
                    G_PlayEffectID( G_EffectIndex("tusken/hit"), trace->endpos, trace->plane.normal );
                }
                ent->s.hasLookTarget = qtrue;

                ent->enemy = other;
                other->s.otherEntityNum = ent->parent->s.number;

                v[0] = other->r.currentOrigin[0];// + (other->r.mins[0] + other->r.maxs[0]) * 0.5;
                v[1] = other->r.currentOrigin[1];// + (other->r.mins[1] + other->r.maxs[1]) * 0.5;
                v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5;

                SnapVectorTowards( v, ent->s.pos.trBase );	// save net bandwidth
                ent->s.otherEntityNum = ent->enemy->s.clientNum;
                other->s.otherEntityNum = ent->parent->s.clientNum;
            } else {
                if ( !strcmp(other->classname, "func_rotating") || !strcmp(other->classname, "func_pendulum") ) {
                    Weapon_HookFree(ent);	// don't work
                    return;
                }
                ent->s.otherEntityNum = other->s.number;
                ent->s.groundEntityNum = other->s.number;
                VectorCopy(trace->endpos, v);
                //G_AddEvent( nent, EV_MISSILE_MISS, 0); //DirToByte( trace->plane.normal ) );				//Event
                if (!ent->s.hasLookTarget) {
                    G_PlayEffectID( G_EffectIndex("tusken/hitwall"), trace->endpos, trace->plane.normal );
                }
                ent->s.hasLookTarget = qtrue;
            }
        } else {
            VectorCopy(trace->endpos, v);
            //G_AddEvent( nent, EV_MISSILE_MISS, 0);//DirToByte( trace->plane.normal ) );						//Event
            if (!ent->s.hasLookTarget) {
                G_PlayEffectID( G_EffectIndex("tusken/hitwall"), trace->endpos, trace->plane.normal );
            }
            ent->s.hasLookTarget = qtrue;
        }

        VectorCopy(trace->plane.normal, ent->s.angles);
        SnapVectorTowards( v, ent->s.pos.trBase );	// save net bandwidth

        // change over to a normal entity right at the point of impact
        //nent->s.eType = ET_GENERAL;
        ent->s.eType = ET_MISSILE;

        G_SetOrigin( ent, v );
        //G_SetOrigin( nent, v );

        ent->think = Weapon_HookThink;
        ent->nextthink = level.time + FRAMETIME;

        VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.lastHitLoc);
        VectorSubtract( ent->r.currentOrigin, ent->parent->client->ps.origin, v );

        trap->LinkEntity( (sharedEntity_t *)ent );
        //trap->LinkEntity( (sharedEntity_t *)nent );

        return;
    }
#endif

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 && !isKnockedSaber ) {
        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 && !isKnockedSaber) {
        G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
    }

    if (!isKnockedSaber)
    {
        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 );

    ent->takedamage = qfalse;
    // 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, ent->splashMethodOfDeath ) ) {
            if( !hitClient
                    && g_entities[ent->r.ownerNum].client ) {
                g_entities[ent->r.ownerNum].client->accuracy_hits++;
            }
        }
    }

    if (ent->s.weapon == G2_MODEL_PART)
    {
        ent->freeAfterEvent = qfalse; //it will free itself
    }

    trap->LinkEntity( (sharedEntity_t *)ent );
}
Esempio n. 21
0
/*
================
G_MissileImpact
	impactDamage is how much damage the impact will do to func_explosives
================
*/
void G_MissileImpact( gentity_t *ent, trace_t *trace, int impactDamage ) {
	gentity_t       *other;
	qboolean hitClient = qfalse;
	vec3_t velocity;
	int etype;

	other = &g_entities[trace->entityNum];

	// handle func_explosives
	if ( other->classname && Q_stricmp( other->classname, "func_explosive" ) == 0 ) {
		// the damage is sufficient to "break" the ent (health == 0 is non-breakable)
		if ( other->health && impactDamage >= other->health ) {
			// check for other->takedamage needs to be inside the health check since it is
			// likely that, if successfully destroyed by the missile, in the next runmissile()
			// update takedamage would be set to '0' and the func_explosive would not be
			// removed yet, causing a bounce.
			if ( other->takedamage ) {
				BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
				G_Damage( other, ent, &g_entities[ent->r.ownerNum], velocity, ent->s.origin, impactDamage, 0, ent->methodOfDeath );
			}

			// its possible of the func_explosive not to die from this and it
			// should reflect the missile or explode it not vanish into oblivion
			if ( other->health <= 0 ) {
				return;
			}
		}
	}

	// check for bounce
	if ( !other->takedamage &&
		 ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
		G_BounceMissile( ent, trace );
		// JPW NERVE -- spotter White Phosphorous rounds shouldn't bounce noise
		if ( !Q_stricmp( ent->classname,"WP" ) ) {
			return;
		}
		// jpw
		if ( !Q_stricmp( ent->classname, "flamebarrel" ) ) {
			G_AddEvent( ent, EV_FLAMEBARREL_BOUNCE, 0 );
		} else {
			G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
		}
		return;
	}

	if ( other->takedamage && ent->s.density == 1 ) {
		G_ExplodeMissilePoisonGas( ent );
		return;
	}

	// impact damage
	if ( other->takedamage ) {
		if ( ent->damage ) {

			if ( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) {
				if ( g_entities[ent->r.ownerNum].client ) {
					g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++;
				}
				hitClient = qtrue;
			}
			BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
			if ( VectorLength( velocity ) == 0 ) {
				velocity[2] = 1;    // stepped on a grenade
			}
			G_Damage( other, ent, &g_entities[ent->r.ownerNum], velocity,
					  ent->s.origin, ent->damage,
					  0, ent->methodOfDeath );
		} else    // if no damage value, then this is a splash damage grenade only
		{
			G_BounceMissile( ent, trace );
			return;
		}
	}

	// 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 {
		// Ridah, try projecting it in the direction it came from, for better decals
		vec3_t dir;
		BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, dir );
		BG_GetMarkDir( dir, trace->plane.normal, dir );
		G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( dir ) );
	}

	ent->freeAfterEvent = qtrue;

	// change over to a normal entity right at the point of impact
	etype = ent->s.eType;
	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 ) {
				g_entities[ent->r.ownerNum].client->ps.persistant[PERS_ACCURACY_HITS]++;
			}
		}
	}

	trap_LinkEntity( ent );
}
Esempio n. 22
0
/*
================
G_MissileImpact

================
*/
void G_MissileImpact( gentity_t *ent, trace_t *trace )
{
    gentity_t   *other, *attacker;
    qboolean    returnAfterDamage = qfalse;
    vec3_t      dir;

    other = &g_entities[ trace->entityNum ];
    attacker = &g_entities[ ent->r.ownerNum ];

    if( !other->takedamage &&
            !strcmp( ent->classname, "bounceball" ) &&
            ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) )
    {
        G_BounceMissile( ent, trace );
        G_AddEvent( ent, EV_MISSILE_MISS, 0 );

        return;
    }

    // check for bounce
    if( !other->takedamage &&
            ( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) )
    {
        G_BounceMissile( ent, trace );

        //only play a sound if requested
        if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
            G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );

        return;
    }

    if( !strcmp( ent->classname, "grenade" ) )
    {
        //grenade doesn't explode on impact
        G_BounceMissile( ent, trace );

        //only play a sound if requested
        if( !( ent->s.eFlags & EF_NO_BOUNCE_SOUND ) )
            G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );

        return;
    }
    else if( !strcmp( ent->classname, "lockblob" ) )
    {
        if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
        {
            other->client->ps.stats[ STAT_STATE ] |= SS_BLOBLOCKED;
            other->client->lastLockTime = level.time;
            AngleVectors( other->client->ps.viewangles, dir, NULL, NULL );
            other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir );
        }
    }
    else if( !strcmp( ent->classname, "slowblob" ) )
    {
        if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
        {
            other->client->ps.stats[ STAT_STATE ] |= SS_SLOWLOCKED;
            other->client->lastSlowTime = level.time;
            AngleVectors( other->client->ps.viewangles, dir, NULL, NULL );
            other->client->ps.stats[ STAT_VIEWLOCK ] = DirToByte( dir );
        }
    }
    else if( !strcmp( ent->classname, "hive" ) )
    {
        if( other->s.eType == ET_BUILDABLE && other->s.modelindex == BA_A_HIVE )
        {
            if( !ent->parent )
                G_Printf( S_COLOR_YELLOW "WARNING: hive entity has no parent in G_MissileImpact\n" );
            else
                ent->parent->active = qfalse;

            G_FreeEntity( ent );
            return;
        }
        else
        {
            //prevent collision with the client when returning
            ent->r.ownerNum = other->s.number;

            ent->think = AHive_ReturnToHive;
            ent->nextthink = level.time + FRAMETIME;

            //only damage humans
            if( other->client && other->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
                returnAfterDamage = qtrue;
            else
                return;
        }
    }

    // impact damage
    if( other->takedamage )
    {
        // FIXME: wrong damage direction?
        if( ent->damage )
        {
            vec3_t  velocity;

            BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
            if( VectorLength( velocity ) == 0 )
                velocity[ 2 ] = 1;  // stepped on a grenade

            G_Damage( other, ent, attacker, velocity, ent->s.origin, ent->damage,
                      0, ent->methodOfDeath );
        }
    }

    if( returnAfterDamage )
        return;

    // 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
        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 )
        G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
                        other, ent->splashMethodOfDeath );

    trap_LinkEntity( ent );
}
Esempio n. 23
0
/*
================
G_MissileImpact
================
*/
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
	gentity_t		*other;
	qboolean		hitClient = qfalse;
#ifdef MISSIONPACK
	vec3_t			forward, impactpoint, bouncedir;
	int				eFlags;
#endif
	other = &g_entities[trace->entityNum];

	// check for bounce
	if ( !other->takedamage &&
		( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
		G_BounceMissile( ent, trace );
		G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
		return;
	}

#ifdef MISSIONPACK
	if ( other->takedamage ) {
		if ( ent->s.weapon != WP_PROX_LAUNCHER ) {
			if ( other->client && other->client->invulnerabilityTime > level.time ) {
				//
				VectorCopy( ent->s.pos.trDelta, forward );
				VectorNormalize( forward );
				if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) {
					VectorCopy( bouncedir, trace->plane.normal );
					eFlags = ent->s.eFlags & EF_BOUNCE_HALF;
					ent->s.eFlags &= ~EF_BOUNCE_HALF;
					G_BounceMissile( ent, trace );
					ent->s.eFlags |= eFlags;
				}
				ent->target_ent = other;
				return;
			}
		}
	}
#endif
	// 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
			}
			G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
				ent->s.origin, ent->damage, 
				0, ent->methodOfDeath);
		}
	}

#ifdef MISSIONPACK
	if( ent->s.weapon == WP_PROX_LAUNCHER ) {
		if( ent->s.pos.trType != TR_GRAVITY ) {
			return;
		}

		// if it's a player, stick it on to them (flag them and remove this entity)
		if( other->s.eType == ET_PLAYER && other->health > 0 ) {
			ProximityMine_Player( ent, other );
			return;
		}

		SnapVectorTowards( trace->endpos, ent->s.pos.trBase );
		G_SetOrigin( ent, trace->endpos );
		ent->s.pos.trType = TR_STATIONARY;
		VectorClear( ent->s.pos.trDelta );

		G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags );

		ent->think = ProximityMine_Activate;
		ent->nextthink = level.time + 2000;

		vectoangles( trace->plane.normal, ent->s.angles );
		ent->s.angles[0] += 90;

		// link the prox mine to the other entity
		ent->enemy = other;
		ent->die = ProximityMine_Die;
		VectorCopy(trace->plane.normal, ent->movedir);
		VectorSet(ent->r.mins, -4, -4, -4);
		VectorSet(ent->r.maxs, 4, 4, 4);
		trap_LinkEntity(ent);

		return;
	}
#endif

	if (!strcmp(ent->classname, "hook")) {
		gentity_t *nent;
		vec3_t v;

		nent = G_Spawn();
		if ( other->takedamage && other->client ) {

			G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
			nent->s.otherEntityNum = other->s.number;

			ent->enemy = other;

			v[0] = other->r.currentOrigin[0] + (other->r.mins[0] + other->r.maxs[0]) * 0.5;
			v[1] = other->r.currentOrigin[1] + (other->r.mins[1] + other->r.maxs[1]) * 0.5;
			v[2] = other->r.currentOrigin[2] + (other->r.mins[2] + other->r.maxs[2]) * 0.5;

			SnapVectorTowards( v, ent->s.pos.trBase );	// save net bandwidth
		} else {
			VectorCopy(trace->endpos, v);
			G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
			ent->enemy = NULL;
		}

		SnapVectorTowards( v, ent->s.pos.trBase );	// save net bandwidth

		nent->freeAfterEvent = qtrue;
		// change over to a normal entity right at the point of impact
		nent->s.eType = ET_GENERAL;
		ent->s.eType = ET_GRAPPLE;

		G_SetOrigin( ent, v );
		G_SetOrigin( nent, v );

		ent->think = Weapon_HookThink;
		ent->nextthink = level.time + FRAMETIME;

		ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL;
		VectorCopy( ent->r.currentOrigin, ent->parent->client->ps.grapplePoint);

		trap_LinkEntity( ent );
		trap_LinkEntity( nent );

		return;
	}

	// 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 {
		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++;
			}
		}
	}

	trap_LinkEntity( ent );
}
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
	gentity_t		*other;
	qboolean		hitClient = qfalse;
	qboolean		isKnockedSaber = qfalse;

	other = &g_entities[trace->entityNum];

	// check for bounce
	if ( !other->takedamage &&
		(ent->bounceCount > 0 || ent->bounceCount == -5) &&
		( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) {
		int originalBounceCount = ent->bounceCount;
		G_BounceMissile( ent, trace );
		if ( originalBounceCount != ent->bounceCount )
		{
		    G_GrenadeBounceEvent ((const gentity_t *)ent);
		}
		return;
	}
	else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF))
	{ //this is a knocked-away saber
		if (ent->bounceCount > 0 || ent->bounceCount == -5)
		{
		    	int originalBounceCount = ent->bounceCount;
		    	G_BounceMissile( ent, trace );
		    	G_GrenadeBounceEvent ((const gentity_t *)ent);
		    	if ( originalBounceCount != ent->bounceCount )
	        	{
	            		G_GrenadeBounceEvent ((const gentity_t *)ent);
	        	}
			return;
		}

		isKnockedSaber = qtrue;
	}
	
	// I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else
	if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_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->flags &= ~FL_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) && !isKnockedSaber)
	{ //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 (!isKnockedSaber)
	{
		if (other->takedamage && other->client && other->client->ps.duelInProgress &&
			other->client->ps.duelIndex != ent->r.ownerNum)
		{
			goto killProj;
		}
	}

	if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY)
	{
		if (ent->methodOfDeath != MOD_REPEATER_ALT &&
			ent->methodOfDeath != MOD_ROCKET &&
			ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
			ent->methodOfDeath != MOD_ROCKET_HOMING &&
			ent->methodOfDeath != MOD_THERMAL &&
			ent->methodOfDeath != MOD_THERMAL_SPLASH &&
			ent->methodOfDeath != MOD_TRIP_MINE_SPLASH &&
			ent->methodOfDeath != MOD_TIMED_MINE_SPLASH &&
			ent->methodOfDeath != MOD_DET_PACK_SPLASH &&
			ent->methodOfDeath != MOD_VEHICLE &&
			ent->methodOfDeath != MOD_CONC &&
			ent->methodOfDeath != MOD_CONC_ALT &&
			ent->methodOfDeath != MOD_SABER &&
			ent->methodOfDeath != MOD_TURBLAST)
		{
			vec3_t fwd;

			if (trace)
			{
				VectorCopy(trace->plane.normal, fwd);
			}
			else
			{ //oh well
				AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
			}

			G_DeflectMissile(other, ent, fwd);
			G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
			return;
		}
	}

	if ((other->flags & FL_SHIELDED) &&
		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_EMPLACED_GUN &&
		ent->methodOfDeath != MOD_REPEATER_ALT &&
		ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && 
		ent->methodOfDeath != MOD_TURBLAST &&
		ent->methodOfDeath != MOD_VEHICLE &&
		ent->methodOfDeath != MOD_CONC &&
		ent->methodOfDeath != MOD_CONC_ALT &&
		!(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) )
	{// entity is shielded
		vec3_t fwd;

		if (other->client)
		{
			AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL);
		}
		else
		{
			AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
		}

		G_DeflectMissile(other, ent, fwd);
		G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
		return;
	}

	// SABERFIXME: make this based on .wpn file? some conc rifles should be able to be deflected...
	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->methodOfDeath != MOD_REPEATER_ALT &&
		ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
		ent->methodOfDeath != MOD_CONC &&
		ent->methodOfDeath != MOD_CONC_ALT &&
		other->client->saberBlockDebounce < level.time &&
		!isKnockedSaber &&
		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_SABER_DEFENSE];

		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;
		te->s.weapon = 0;//saberNum
		te->s.legsAnim = 0;//bladeNum

		/*if (other->client->ps.velocity[2] > 0 ||
			other->client->pers.cmd.forwardmove ||
			other->client->pers.cmd.rightmove)
			*/
		if (other->client->ps.velocity[2] > 0 ||
			other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge.
		{
			otherDefLevel -= 1;
			if (otherDefLevel < 0)
			{
				otherDefLevel = 0;
			}
		}

		AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL);
		// SABERFIXME: Don't make this force based. This code is ugly as f**k anyway
		other->client->saberBlockDebounce = level.time + (350 - (otherDefLevel*100)); //200;

		//For jedi AI
		other->client->ps.saberEventFlags |= SEF_DEFLECTED;
		if ( other->client->ps.saberActionFlags & (1 << SAF_BLOCKING) && !(other->client->ps.saberActionFlags & ( 1 << SAF_PROJBLOCKING) ) )
		{
			goto killProj;
 		}
		else if ( other->client->ps.saberActionFlags & SAF_PROJBLOCKING )
		{
			JKG_SaberDeflectMissile(other, ent, fwd);
			other->client->saberProjBlockTime += 500; // give them a little bit of leeway --eez
		}
		return;
	}
	else if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber)
	{ //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->methodOfDeath != MOD_REPEATER_ALT &&
			ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
			ent->methodOfDeath != MOD_CONC &&
			ent->methodOfDeath != MOD_CONC_ALT /*&&
			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_SABER_DEFENSE];

			//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);
			if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0)
			{
				WP_SaberBlockNonRandom(otherOwner, NULL, ent->r.currentOrigin, qtrue);		// <-- ??? --eez
			}

			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;
			te->s.weapon = 0;//saberNum
			te->s.legsAnim = 0;//bladeNum

			/*if (otherOwner->client->ps.velocity[2] > 0 ||
				otherOwner->client->pers.cmd.forwardmove ||
				otherOwner->client->pers.cmd.rightmove)*/
			if (otherOwner->client->ps.velocity[2] > 0 ||
				otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge.
			{
				otherDefLevel -= 1;
				if (otherDefLevel < 0)
				{
					otherDefLevel = 0;
				}
			}

			AngleVectors(otherOwner->client->ps.viewangles, fwd, NULL, NULL);
			otherOwner->client->saberBlockDebounce = level.time + (350 - (otherDefLevel*100));//200;

			//For jedi AI
			otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED;

			// um...this code is a lil messed up, so i'll replace it with my own --eez
			if ( otherOwner->client->ps.saberActionFlags & (1 << SAF_BLOCKING) && !(otherOwner->client->ps.saberActionFlags & ( 1 << SAF_PROJBLOCKING )) )
			{
				goto killProj;
 			}
			else if ( otherOwner->client->ps.saberActionFlags & ( 1 << SAF_PROJBLOCKING ) )
			{
				// SABERFIXME: Write new function for this
				JKG_SaberDeflectMissile(otherOwner, ent, fwd);
				otherOwner->client->saberProjBlockTime += 500; // give them a little bit of leeway --eez
			}

			return;
		}
	}

	if( ent->genericValue10 )	// nerf this check in order to make grenade bouncing work --eez
	{
		vec3_t fwd;

		if (other->client)
		{
			AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL);
		}
		else
		{
			AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
		}
		VectorScale(ent->s.pos.trDelta, 0.2, ent->s.pos.trDelta);
		G_Damage(other, ent, ent->parent, fwd, ent->s.origin, ent->genericValue9, 0, MOD_THERMAL);
		G_DeflectMissile(other, ent, fwd);
		//G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
		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 && !isKnockedSaber) {
		// FIXME: wrong damage direction?
		weaponData_t *weapon = GetWeaponData (ent->s.weapon, ent->s.weaponVariation);
		weaponFireModeStats_t *fireMode = &weapon->firemodes[ent->s.firingMode];
		
		if ( ent->damage || fireMode->damageTypeHandle || fireMode->secondaryDmgHandle ) {
			vec3_t	velocity;

			hitClient = qtrue;
			BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
			if ( VectorLength( velocity ) == 0 ) {
				velocity[2] = 1;	// stepped on a grenade
			}

            if ( fireMode->damageTypeHandle )
            {
                JKG_DoDamage (fireMode->damageTypeHandle, other, ent, &g_entities[ent->r.ownerNum], velocity, ent->r.currentOrigin, 0, ent->methodOfDeath);
            }
            else if ( ent->damage )
            {
                G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, ent->r.currentOrigin, ent->damage, 0, ent->methodOfDeath);
            }
            
            if ( fireMode->secondaryDmgHandle )
            {
                JKG_DoDamage (fireMode->secondaryDmgHandle, other, ent, &g_entities[ent->r.ownerNum], velocity, ent->r.currentOrigin, 0, ent->methodOfDeath);
            }

			if (other->client)
			{ //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever.
				class_t	npc_class = other->client->NPC_class;

				// If we are a robot and we aren't currently doing the full body electricity...
				if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE ||
					   npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE ||
					   npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd
					   npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY )
				{
					// special droid only behaviors
					if ( other->client->ps.electrifyTime < level.time + 100 )
					{
						// ... do the effect for a split second for some more feedback
						other->client->ps.electrifyTime = level.time + 450;
					}
					//FIXME: throw some sparks off droids,too
				}
			}
		}
	}
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 && !isKnockedSaber ) {
		{
			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 && !isKnockedSaber) {
		G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
	}

	if (!isKnockedSaber)
	{
		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 );

	ent->takedamage = qfalse;

	if (ent->s.weapon == G2_MODEL_PART)
	{
		ent->freeAfterEvent = qfalse; //it will free itself
	}

	if(ent->splashRadius && ent->splashDamage && !ent->genericValue10)
	{
		G_RadiusDamage(trace->endpos, &g_entities[ent->r.ownerNum], ent->splashDamage, ent->splashRadius, NULL, ent, ent->methodOfDeath);
	}

	trap->LinkEntity( (sharedEntity_t *)ent );
}
Esempio n. 25
0
/*
================
CG_AddMissile
================
*/
static void CG_AddMissile( localEntity_t *le ) {
	refEntity_t			*re;
	const weaponInfo_t	*weapon;
	trace_t				trace;
	centity_t			*other;
	qboolean			inWater;

	// just existing for server entity deletion
	if ( le->leFlags & LEF_FINISHED ) {
		return;
	}

	// get weapon info
	if ( le->ti.weapon > WP_NUM_WEAPONS ) {
		le->ti.weapon = 0;
	}
	weapon = &cg_weapons[le->ti.weapon];

	re = &le->refEntity;

	// calculate position
	BG_EvaluateTrajectory( &le->pos, cg.time, re->origin );

	// special case for flames
	if ( re->reType == RT_SPRITE ) {
		int deltaTime;

		// check for water
		if ( trap_CM_PointContents( re->origin, 0 ) & CONTENTS_WATER ) {
			// do a trace to get water surface normals
			CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_WATER );
			CG_FreeLocalEntity( le );

			CG_MakeExplosion( trace.endpos, trace.plane.normal, 
				cgs.media.ringFlashModel, cgs.media.vaporShader,
				500, qfalse, qtrue );
			return;
		}

		// change radius over time
		deltaTime = cg.time - le->startTime;
		re->radius = deltaTime * deltaTime * ( random()*0.4f + 0.8f ) / 2000.0f + 9;

		// do a trace sometimes
		if ( le->ti.trailTime++ > 5 ) {
			le->ti.trailTime = 0;

			CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT );
			VectorCopy( re->origin, re->oldorigin );

			// hit something
			if ( trace.fraction < 1.0 ) {
				CG_MissileHitWall( le->ti.weapon, 0, trace.endpos, trace.plane.normal, IMPACTSOUND_DEFAULT );
				CG_FreeLocalEntity( le );
				return;
			}
		}

		// add to refresh list
		trap_R_AddRefEntityToScene( re );
		return;
	}

	// add trails
	if ( weapon->missileTrailFunc ) weapon->missileTrailFunc( &le->ti, cg.time );

	// add dynamic light
	if ( weapon->missileDlight ) {
		trap_R_AddLightToScene( re->origin, weapon->missileDlight, 
			weapon->missileDlightColor[0], weapon->missileDlightColor[1], weapon->missileDlightColor[2] );
	}

	// flicker between two skins
	re->skinNum = cg.clientFrame & 1;

	// convert direction of travel into axis
	if ( VectorNormalize2( le->pos.trDelta, re->axis[0] ) == 0 ) {
		re->axis[0][2] = 1;
	}

	// spin as it moves
	if ( le->pos.trType != TR_STATIONARY ) {
		if ( le->pos.trType == TR_GRAVITY ) {
			RotateAroundDirection( re->axis, cg.time / 4 );
		} else if ( le->pos.trType == TR_WATER_GRAVITY ) {
			RotateAroundDirection( re->axis, cg.time / 8 );
		} else {
			RotateAroundDirection( re->axis, cg.time );
		}
	} else {
		RotateAroundDirection( re->axis, 0 );
	}

	// trace a line from previous position to new position
	CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT );
	VectorCopy( re->origin, re->oldorigin );

	// draw BIG grenades
	if ( weLi[le->ti.weapon].category == CT_EXPLOSIVE ) {
		AxisScale( re->axis, GRENADE_SCALE, re->axis );
	}

	if ( trace.fraction != 1.0 ) {
		// hit the sky or something like that
		if ( trace.surfaceFlags & SURF_NOIMPACT ) {
			le->leFlags |= LEF_FINISHED;
			le->endTime = cg.time + 500;
			return;
		}

		// impact
		other = &cg_entities[trace.entityNum];

		if ( le->bounceFactor > 0 && ( le->bounceFactor == BOUNCE_FACTOR_HALF || other->currentState.eType != ET_PLAYER ) ) {
			// reflect the velocity on the trace plane
			CG_ReflectVelocity( le, &trace );
			if ( cg.predictedImpacts < MAX_PREDICTED_IMPACTS ) {
				cg.predictedImpacts++;
				cg.predictedImpactsDecTime = cg.time;
			}

			// do bounce sound
			if ( rand() & 1 ) {
				trap_S_StartSound( le->pos.trBase, 0, CHAN_AUTO, cgs.media.hgrenb1aSound );
			} else {
				trap_S_StartSound( le->pos.trBase, 0, CHAN_AUTO, cgs.media.hgrenb2aSound );
			}
		} else {
			// explode missile
			if ( cg.predictedImpacts < MAX_PREDICTED_IMPACTS ) {
				cg.predictedImpacts++;
				cg.predictedImpactsDecTime = cg.time;
			}

			if ( other->currentState.eType == ET_PLAYER ) {
				CG_MissileHitPlayer( le->ti.weapon, 0, trace.endpos, trace.plane.normal, trace.entityNum );
			} else if ( !(trace.surfaceFlags & SURF_NOIMPACT) ) {
				CG_MissileHitWall( le->ti.weapon, 0, trace.endpos, trace.plane.normal, 
					(trace.surfaceFlags & SURF_METALSTEPS) ? IMPACTSOUND_METAL : IMPACTSOUND_DEFAULT );
			}
			// store the entity for deleting the server entity
			le->leFlags |= LEF_FINISHED;
			le->endTime = cg.time + 500;
			return;
		}
	}

	// check for medium change
	if ( trap_CM_PointContents( re->origin, 0 ) & CONTENTS_WATER ) inWater = qtrue;
	else inWater = qfalse;

	if (  ( !inWater && le->pos.trType == TR_WATER_GRAVITY )
		|| ( inWater && le->pos.trType == TR_GRAVITY ) ) {
		// setup new tr
		vec3_t	newDelta;

		BG_EvaluateTrajectoryDelta( &le->pos, cg.time, newDelta );
		VectorCopy( re->origin, le->pos.trBase );
		VectorCopy( newDelta, le->pos.trDelta );
		le->pos.trTime = cg.time;
		if ( inWater ) le->pos.trType = TR_WATER_GRAVITY;
		else le->pos.trType = TR_GRAVITY;
	}	

	// add to refresh list
	trap_R_AddRefEntityToScene( re );
}
Esempio n. 26
0
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
	gentity_t		*other;
	qboolean		hitClient = qfalse;
	qboolean		isKnockedSaber = qfalse;

	other = &g_entities[trace->entityNum];

	// check for bounce
	if ( !other->takedamage &&
		(ent->bounceCount > 0 || ent->bounceCount == -5) &&
		(ent->flags & (FL_BOUNCE | FL_BOUNCE_HALF)) ) {
		G_BounceMissile( ent, trace );
		G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
		return;
	}
	else if ( ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF) ) { //this is a knocked-away saber
		if ( ent->bounceCount > 0 || ent->bounceCount == -5 ) {
			G_BounceMissile( ent, trace );
			G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
			return;
		}

		isKnockedSaber = qtrue;
	}

	// I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else
	if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && (ent->flags&(FL_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->flags &= ~FL_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) && !isKnockedSaber ) { //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 ( !isKnockedSaber ) {
		if ( other->takedamage && other->client && other->client->ps.duelInProgress &&
			other->client->ps.duelIndex != ent->r.ownerNum ) {
			goto killProj;
		}
	}

	if ( other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY ) {
		if ( ent->methodOfDeath != MOD_REPEATER_ALT &&
			ent->methodOfDeath != MOD_ROCKET &&
			ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
			ent->methodOfDeath != MOD_ROCKET_HOMING &&
			ent->methodOfDeath != MOD_THERMAL &&
			ent->methodOfDeath != MOD_THERMAL_SPLASH &&
			ent->methodOfDeath != MOD_TRIP_MINE_SPLASH &&
			ent->methodOfDeath != MOD_TIMED_MINE_SPLASH &&
			ent->methodOfDeath != MOD_DET_PACK_SPLASH &&
			ent->methodOfDeath != MOD_VEHICLE &&
			ent->methodOfDeath != MOD_CONC &&
			ent->methodOfDeath != MOD_CONC_ALT &&
			ent->methodOfDeath != MOD_SABER &&
			ent->methodOfDeath != MOD_TURBLAST ) {
			vector3 fwd;

			if ( trace ) {
				VectorCopy( &trace->plane.normal, &fwd );
			}
			else { //oh well
				AngleVectors( &other->r.currentAngles, &fwd, NULL, NULL );
			}

			G_DeflectMissile( other, ent, &fwd );
			G_MissileBounceEffect( ent, &ent->r.currentOrigin, &fwd );
			return;
		}
	}

	if ( (other->flags & FL_SHIELDED) &&
		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->s.weapon != WP_EMPLACED_GUN &&
		ent->methodOfDeath != MOD_REPEATER_ALT &&
		ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
		ent->methodOfDeath != MOD_TURBLAST &&
		ent->methodOfDeath != MOD_VEHICLE &&
		ent->methodOfDeath != MOD_CONC &&
		ent->methodOfDeath != MOD_CONC_ALT &&
		!(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) ) {
		vector3 fwd;

		if ( other->client ) {
			AngleVectors( &other->client->ps.viewangles, &fwd, NULL, NULL );
		}
		else {
			AngleVectors( &other->r.currentAngles, &fwd, NULL, NULL );
		}

		G_DeflectMissile( other, ent, &fwd );
		G_MissileBounceEffect( ent, &ent->r.currentOrigin, &fwd );
		return;
	}

	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 &&
		ent->methodOfDeath != MOD_REPEATER_ALT &&
		ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
		ent->methodOfDeath != MOD_CONC &&
		ent->methodOfDeath != MOD_CONC_ALT &&
		other->client->ps.saberBlockTime < level.time &&
		!isKnockedSaber &&
		WP_SaberCanBlock( other, &ent->r.currentOrigin, 0, 0, qtrue, 0 ) ) { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked)
		vector3 fwd;
		gentity_t *te;
		int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE];

		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;
		te->s.weapon = 0;//saberNum
		te->s.legsAnim = 0;//bladeNum

		/*if (other->client->ps.velocity[2] > 0 ||
			other->client->pers.cmd.forwardmove ||
			other->client->pers.cmd.rightmove)
			*/
		if ( other->client->ps.velocity.z > 0 ||
			other->client->pers.cmd.forwardmove < 0 ) //now we only do it if jumping or running backward. Should be able to full-on charge.
		{
			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;

		//For jedi AI
		other->client->ps.saberEventFlags |= SEF_DEFLECTED;

		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) && !isKnockedSaber ) { //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 &&
			ent->methodOfDeath != MOD_CONC &&
			ent->methodOfDeath != MOD_CONC_ALT /*&&
			otherOwner->client->ps.saberBlockTime < level.time*/ ) { //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber
			vector3 fwd;
			gentity_t *te;
			int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE];

			//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);
			if ( otherOwner->client && otherOwner->client->ps.weaponTime <= 0 ) {
				WP_SaberBlockNonRandom( otherOwner, &ent->r.currentOrigin, qtrue );
			}

			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;
			te->s.weapon = 0;//saberNum
			te->s.legsAnim = 0;//bladeNum

			/*if (otherOwner->client->ps.velocity[2] > 0 ||
				otherOwner->client->pers.cmd.forwardmove ||
				otherOwner->client->pers.cmd.rightmove)*/
			if ( otherOwner->client->ps.velocity.z > 0 ||
				otherOwner->client->pers.cmd.forwardmove < 0 ) //now we only do it if jumping or running backward. Should be able to full-on charge.
			{
				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;

			//For jedi AI
			otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED;

			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 && !isKnockedSaber ) {
		// FIXME: wrong damage direction?
		if ( ent->damage ) {
			vector3	velocity;
			qboolean didDmg = qfalse;

			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.z = 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 );
					JPLua::Entity_CallFunction( ent, JPLua::JPLUA_ENTITY_THINK );
				}
				else {
					G_Damage( other, ent, &g_entities[ent->r.ownerNum], &velocity,
						/*ent->s.origin*/&ent->r.currentOrigin, ent->damage,
						DAMAGE_HALF_ABSORB, ent->methodOfDeath );
					didDmg = qtrue;
				}
			}
			else {
				G_Damage( other, ent, &g_entities[ent->r.ownerNum], &velocity,
					/*ent->s.origin*/&ent->r.currentOrigin, ent->damage,
					0, ent->methodOfDeath );
				didDmg = qtrue;
			}

			//Raz: air shots
			if ( (other->client && other->client->ps.groundEntityNum == ENTITYNUM_NONE) &&
				(ent->methodOfDeath == MOD_CONC ||
				ent->methodOfDeath == MOD_REPEATER_ALT ||
				ent->methodOfDeath == MOD_ROCKET ||
				ent->methodOfDeath == MOD_ROCKET_HOMING ||
				ent->methodOfDeath == MOD_THERMAL) ) {
				g_entities[ent->r.ownerNum].client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
			}

			if ( didDmg && other && other->client ) { //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever.
				class_t	npc_class = other->client->NPC_class;

				// If we are a robot and we aren't currently doing the full body electricity...
				if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE ||
					npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE ||
					npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd
					npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) {
					// special droid only behaviors
					if ( other->client->ps.electrifyTime < level.time + 100 ) {
						// ... do the effect for a split second for some more feedback
						other->client->ps.electrifyTime = level.time + 450;
					}
					//FIXME: throw some sparks off droids,too
				}
			}
		}

		if ( ent->s.weapon == WP_DEMP2 ) {//a hit with demp2 decloaks people, disables ships
			if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE ) {//hit a vehicle
				if ( other->m_pVehicle //valid vehicle ent
					&& other->m_pVehicle->m_pVehicleInfo//valid stats
					&& (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders
					|| (other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp( "vehicle_proj", ent->classname ) == 0))//only vehicle ion weapons affect a fighter in this manner
					&& !FighterIsLanded( other->m_pVehicle, &other->client->ps )//not landed
					&& !(other->spawnflags & 2) )//and not suspended
				{//vehicles hit by "ion cannons" lose control
					if ( other->client->ps.electrifyTime > level.time ) {//add onto it
						//FIXME: extern the length of the "out of control" time?
						other->client->ps.electrifyTime += Q_irand( 200, 500 );
						if ( other->client->ps.electrifyTime > level.time + 4000 ) {//cap it
							other->client->ps.electrifyTime = level.time + 4000;
						}
					}
					else {//start it
						//FIXME: extern the length of the "out of control" time?
						other->client->ps.electrifyTime = level.time + Q_irand( 200, 500 );
					}
				}
			}
			else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] ) {
				Jedi_Decloak( other );
				if ( ent->methodOfDeath == MOD_DEMP2_ALT ) {//direct hit with alt disables cloak forever
					//permanently disable the saboteur's cloak
					other->client->cloakToggleTime = Q3_INFINITE;
				}
				else {//temp disable
					other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 );
				}
			}
		}
	}
killProj:

	if ( !strcmp( ent->classname, "hook" ) ) {
		//	gentity_t	*nent = G_Spawn();
		vector3		v;
		int			i;

		if ( other->takedamage && other->client ) {
			//	G_AddEvent( nent, EV_DISRUPTOR_HIT, DirToByte( trace->plane.normal ) );
			//	nent->s.otherEntityNum = other->s.number;

			ent->enemy = other;

			for ( i = 0; i < 3; i++ )
				v.raw[i] = other->r.currentOrigin.raw[i] + (other->r.mins.raw[i] + other->r.maxs.raw[i]) * 0.5f;
			SnapVectorTowards( &v, &ent->s.pos.trBase );	//	Save net bandwidth
		}
		else {
			VectorCopy( &trace->endpos, &v );
			//	G_AddEvent( nent, EV_DISRUPTOR_HIT, DirToByte( trace->plane.normal ) );
			ent->enemy = NULL;
		}

		SnapVectorTowards( &v, &ent->s.pos.trBase );	//	Save net bandwidth

		//	nent->freeAfterEvent = true;

		//Change over to a normal entity right at the point of impact
		//	nent->s.eType = ET_GENERAL;
		//	ent->s.eType = ET_GENERAL;

		G_SetOrigin( ent, &v );
		//	G_SetOrigin( nent, v );

		ent->think = Weapon_HookThink;
		ent->nextthink = level.time + FRAMETIME;

		//	ent->parent->client->ps.pm_flags |= PMF_GRAPPLE_PULL;
		//	ent->parent->client->ps.eFlags |= EF_GRAPPLE_SWING;
		//ent->genericValue10 = 1;
		ent->parent->client->fireHeld = qfalse;
		VectorCopy( &ent->r.currentOrigin, &ent->parent->client->ps.lastHitLoc );

		trap->LinkEntity( (sharedEntity_t *)ent );
		//	trap->LinkEntity( (sharedEntity_t *)nent );

		return;
	}

	// 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 && !isKnockedSaber ) {
		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 && !isKnockedSaber ) {
		G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( &trace->plane.normal ) );
	}

	if ( !isKnockedSaber ) {
		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 );

	ent->takedamage = qfalse;
	// 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, ent->splashMethodOfDeath ) ) {
			if ( !hitClient
				&& g_entities[ent->r.ownerNum].client ) {
				g_entities[ent->r.ownerNum].client->accuracy_hits++;
			}
		}
	}

	if ( ent->s.weapon == G2_MODEL_PART ) {
		ent->freeAfterEvent = qfalse; //it will free itself
	}

	trap->LinkEntity( (sharedEntity_t *)ent );
}
Esempio n. 27
0
/*
================
G_MissileImpact
================
*/
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
	gentity_t		*other;
	qboolean		hitPlayer = qfalse;
#if defined MISSIONPACK && !defined TURTLEARENA // POWERS
	vec3_t			forward, impactpoint, bouncedir;
	int				eFlags;
#endif
#ifdef TA_WEAPSYS
	qboolean damagedOther = qfalse;
#endif
	other = &g_entities[trace->entityNum];

#if defined MISSIONPACK && !defined TURTLEARENA // POWERS
	if ( other->takedamage ) {
#ifdef TA_WEAPSYS
		if ( !bg_projectileinfo[ent->s.weapon].stickOnImpact )
#else
		if ( ent->s.weapon != WP_PROX_LAUNCHER )
#endif
		{
			if ( other->player && other->player->invulnerabilityTime > level.time ) {
				//
				VectorCopy( ent->s.pos.trDelta, forward );
				VectorNormalize( forward );
				if (G_InvulnerabilityEffect( other, forward, ent->s.pos.trBase, impactpoint, bouncedir )) {
					VectorCopy( bouncedir, trace->plane.normal );
					eFlags = ent->s.eFlags & EF_BOUNCE_HALF;
					ent->s.eFlags &= ~EF_BOUNCE_HALF;
					G_BounceMissile( ent, trace );
					ent->s.eFlags |= eFlags;
				}
				ent->target_ent = other;
				return;
			}
		}
	}
#endif
	// impact damage
	if (other->takedamage
#ifdef TA_WEAPSYS // stickOnImpact only damages once
		&& !(ent->count & 2)
#endif
		)
	{
		// FIXME: wrong damage direction?
		if ( ent->damage ) {
			vec3_t	velocity;

			if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) {
				g_entities[ent->r.ownerNum].player->accuracy_hits++;
				hitPlayer = qtrue;
			}
			BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity );
			if ( VectorLength( velocity ) == 0 ) {
#ifdef IOQ3ZTM
				VectorCopy(trace->plane.normal, velocity);
#else
				velocity[2] = 1;	// stepped on a grenade
#endif
			}
#ifdef TA_WEAPSYS
			damagedOther = G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
				ent->s.origin, ent->damage,
				0, ent->methodOfDeath);
#else
			G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
				ent->s.origin, ent->damage, 
				0, ent->methodOfDeath);
#endif
		}
	}

	// check for bounce
	if (
#ifdef TA_WEAPSYS
		!damagedOther &&
#else
		!other->takedamage &&
#endif
		( ent->s.eFlags & ( EF_BOUNCE | EF_BOUNCE_HALF ) ) ) {
		G_BounceMissile( ent, trace );
#ifdef TA_WEAPSYS // Bounce missiles
		// Die on Nth bounce
		if (ent->s.modelindex2 > 0)
		{
			ent->s.modelindex2--;
			if (ent->s.modelindex2 == 0)
			{
				// Kill missile
				G_ExplodeMissile( ent );
				return;
			}
		}
		G_AddEvent( ent, EV_PROJECTILE_BOUNCE, DirToByte( trace->plane.normal ) );
		ent->s.time2 = trace->surfaceFlags; // surface
#else
		G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
#endif
		return;
	}

#ifdef TA_WEAPSYS
	if (bg_projectileinfo[ent->s.weapon].stickOnImpact != PSOI_NONE) {
		vec3_t dir;

#ifndef TURTLEARENA
		// if it's a player, stick it on to them (flag them and remove this entity)
		if( bg_projectileinfo[ent->s.weapon].explosionType == PE_PROX &&
			other->s.eType == ET_PLAYER && other->health > 0 )
		{
			ProximityMine_Player( ent, other );
			return;
		}
#endif

		if (ent->count & 2) {
			// Already stuck to wall
			return;
		}
		ent->count |= 2;

		// Don't stick to players or obelisk
		if (other->s.eType == ET_PLAYER
#ifdef MISSIONPACK
			|| (other->pain == ObeliskPain)
#endif
			)
		{
			goto missileExplode;
		}

		// Don't stick to the entity that this missile just killed or other missiles
		if ((damagedOther && other->health <= 0) || other->s.eType == ET_MISSILE)
		{
			// Don't remove projectile if it doesn't explode.
			if (bg_projectileinfo[ent->s.weapon].explosionType == PE_NONE)
			{
				ent->s.pos.trType = TR_GRAVITY;
				ent->count &= ~2;
				return;
			}
			else
			{
				goto missileExplode;
			}
		}

		if (bg_projectileinfo[ent->s.weapon].shootable)
			VectorMA(trace->endpos, -8, trace->plane.normal, trace->endpos);

		SnapVectorTowards( trace->endpos, ent->s.pos.trBase );
		G_SetOrigin( ent, trace->endpos );

		if (bg_projectileinfo[ent->s.weapon].stickOnImpact == PSOI_KEEP_ANGLES) {
#if 0
			// convert direction of travel into axis
			if ( VectorNormalize2( ent->s.pos.trDelta, dir ) == 0 ) {
				dir[2] = 1;
			}

			// Set the angles
			vectoangles( dir, ent->s.angles );
#else
			VectorCopy(trace->plane.normal, dir);
#endif
		} else {
			VectorCopy(trace->plane.normal, dir);
			vectoangles( dir, ent->s.angles );

			switch (bg_projectileinfo[ent->s.weapon].stickOnImpact)
			{
				case PSOI_ANGLE_270:
					ent->s.angles[0] += 270;
					break;
				case PSOI_ANGLE_180:
					ent->s.angles[0] += 180;
					break;
				case PSOI_ANGLE_90:
					// Maybe this is good for prox mines, but doesn't look good on my
					//   rocket or shuirkens...
					ent->s.angles[0] += 90;
					break;
				case PSOI_ANGLE_0:
					break;
			}
		}

		// Save direction
		VectorCopy(dir, ent->s.angles2);

		ent->s.pos.trType = TR_STATIONARY;
		VectorClear( ent->s.pos.trDelta );

		G_AddEvent( ent, EV_PROJECTILE_STICK, DirToByte(trace->plane.normal) );
		ent->s.time2 = trace->surfaceFlags; // surface

		if (bg_projectileinfo[ent->s.weapon].explosionType == PE_PROX)
		{
			// When a BREAKABLE ET_MOVER is killed it drops the projectiles stuck to it,
			//   so don't setup the prox mine when it impact a surface if it already hit been setup.
			if (ent->die != ProximityMine_Die)
			{
				ent->think = ProximityMine_Activate;
				ent->nextthink = level.time + 2000;
				ent->die = ProximityMine_Die;
			}
		}
		else
		{
			ent->die = G_Missile_Die;
		}

		// link the prox mine to the other entity
		ent->enemy = other;
		VectorCopy(dir, ent->movedir);
		VectorSet(ent->s.mins, -4, -4, -4);
		VectorSet(ent->s.maxs, 4, 4, 4);
		trap_LinkEntity(ent);

		return;
	}
#elif defined MISSIONPACK
	if( ent->s.weapon == WP_PROX_LAUNCHER ) {
		if( ent->s.pos.trType != TR_GRAVITY ) {
			return;
		}

		// if it's a player, stick it on to them (flag them and remove this entity)
		if( other->s.eType == ET_PLAYER && other->health > 0 ) {
			ProximityMine_Player( ent, other );
			return;
		}

		SnapVectorTowards( trace->endpos, ent->s.pos.trBase );
		G_SetOrigin( ent, trace->endpos );
		ent->s.pos.trType = TR_STATIONARY;
		VectorClear( ent->s.pos.trDelta );

		G_AddEvent( ent, EV_PROXIMITY_MINE_STICK, trace->surfaceFlags );

		ent->think = ProximityMine_Activate;
		ent->nextthink = level.time + 2000;

		vectoangles( trace->plane.normal, ent->s.angles );
		ent->s.angles[0] += 90;

		// link the prox mine to the other entity
		ent->enemy = other;
		ent->die = ProximityMine_Die;
		VectorCopy(trace->plane.normal, ent->movedir);
		VectorSet(ent->s.mins, -4, -4, -4);
		VectorSet(ent->s.maxs, 4, 4, 4);
		trap_LinkEntity(ent);

		return;
	}
#endif

#ifdef TA_WEAPSYS
	if (bg_projectileinfo[ent->s.weapon].grappling)
#else
	if (!strcmp(ent->classname, "hook"))
#endif
	{
		gentity_t *nent;
		vec3_t v;

		nent = G_Spawn();
		if ( other->takedamage && other->player ) {

			G_AddEvent( nent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) );
			nent->s.otherEntityNum = other->s.number;

			v[0] = other->r.currentOrigin[0] + (other->s.mins[0] + other->s.maxs[0]) * 0.5;
			v[1] = other->r.currentOrigin[1] + (other->s.mins[1] + other->s.maxs[1]) * 0.5;
			v[2] = other->r.currentOrigin[2] + (other->s.mins[2] + other->s.maxs[2]) * 0.5;
		} else {
			VectorCopy(trace->endpos, v);
			G_AddEvent( nent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
		}
#ifdef IOQ3ZTM // IOQ3BUGFIX: Fix grapple wallmark/death-effect/debris (only tested with TA_WEAPSYS...)
		nent->s.weapon = ent->s.weapon;
#endif
#ifdef TA_WEAPSYS
		if (ent->parent)
			nent->s.playerNum = ent->parent->s.number;
		else
			nent->s.playerNum = ENTITYNUM_NONE;
#endif

		nent->s.weapon = ent->s.weapon;

		ent->enemy = other;
		ent->s.groundEntityNum = other->s.number;

		SnapVectorTowards( v, ent->s.pos.trBase );	// save net bandwidth

		nent->freeAfterEvent = qtrue;
		// change over to a normal entity right at the point of impact
		nent->s.eType = ET_GENERAL;
		ent->s.eType = ET_GRAPPLE;

		G_SetOrigin( ent, v );
		G_SetOrigin( nent, v );

		ent->think = Weapon_HookThink;
		ent->nextthink = level.time + FRAMETIME;

		ent->parent->player->ps.pm_flags |= PMF_GRAPPLE_PULL;
		VectorCopy( ent->r.currentOrigin, ent->parent->player->ps.grapplePoint);

		trap_LinkEntity( ent );
		trap_LinkEntity( nent );

#ifdef TA_WEAPSYS
		// Don't grapple to the entity if you just killed it.
		if (damagedOther && other->health <= 0)
		{
			Weapon_HookFree(ent);
		}
#endif

		return;
	}

#ifdef TA_WEAPSYS
missileExplode:
#endif

	// 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->player ) {
		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 {
		G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
	}
#ifdef TA_WEAPSYS
	if (ent->parent)
		ent->s.playerNum = ent->parent->s.number;
	else
		ent->s.playerNum = ENTITYNUM_NONE;
#endif

	ent->freeAfterEvent = qtrue;

	// change over to a normal entity right at the point of impact
#ifndef TA_WEAPSYS // Must be after G_RadiusDamage
	ent->s.eType = ET_GENERAL;
#endif

	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 ) {
#ifdef TA_WEAPSYS
		if( G_RadiusDamage( trace->endpos, ent, ent->parent, ent->splashDamage, ent->splashRadius, 
			other, ent->splashMethodOfDeath ) )
#else
		if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, 
			other, ent->splashMethodOfDeath ) )
#endif
		{
			if( !hitPlayer ) {
				g_entities[ent->r.ownerNum].player->accuracy_hits++;
			}
		}
	}

#ifdef TA_WEAPSYS
	ent->s.eType = ET_GENERAL;
#endif

	trap_LinkEntity( ent );
}
Esempio n. 28
0
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->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) {
		G_BounceMissile( ent, trace );
	//	G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
		return;
	}

	if ( other->s.eType == ET_MISSILE && ent->s.weapon == WP_QUANTIZER && other->s.weapon == WP_MORTAR )
	{//Quantizer can deflect mortar shots :3
		G_DeflectMissile( ent, other, &ent->s.pos.trDelta );
		other->r.ownerNum = ent->r.ownerNum;
		G_FreeEntity( ent );
		return;
	}

	// impact damage
	if (other->takedamage) {
		// FIXME: wrong damage direction?
		if ( ent->damage ) {
			vector3	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.z = 1;	// stepped on a grenade
			}
			//Raz: The inflictor is the missile, for direct hits from quantizer/RLauncher/etc
			G_Damage( other, ent, &g_entities[ent->r.ownerNum], ent, NULL/*velocity*/, &ent->s.origin, ent->damage, 0, ent->methodOfDeath );
		}
	}

	// 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 {
		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;

	VectorSnapTowards( &trace->endpos, &ent->s.pos.trBase );	// save net bandwidth

	G_SetOrigin( ent, &trace->endpos );

	// splash damage (doesn't apply to person directly hit)
#if 0
	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++;
			}
		}
	}
#else
	//QtZ
	if ( (ent->splashDamage || ent->splashRadius)
		&& G_RadiusDamage( &trace->endpos, ent->parent, (float)ent->splashDamage, (float)ent->splashRadius, other, ent, ent->splashMethodOfDeath )
		&& !hitClient && g_entities[ent->r.ownerNum].client )
		g_entities[ent->r.ownerNum].client->accuracy_hits++;
#endif

	trap->SV_LinkEntity( (sharedEntity_t *)ent );
}
Esempio n. 29
0
void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
	gentity_t		*other;
	qboolean		hitClient = qfalse;
	qboolean		isKnockedSaber = qfalse;

	other = &g_entities[trace->entityNum];

	// check for bounce
	if ( !other->takedamage &&
		(ent->bounceCount > 0 || ent->bounceCount == -5 ) &&
		( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) {
		G_BounceMissile( ent, trace );
		G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
		return;
	}
	else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF))
	{ //this is a knocked-away saber
		if (ent->bounceCount > 0 || ent->bounceCount == -5)
		{
			G_BounceMissile( ent, trace );
			G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
			return;
		}

		isKnockedSaber = qtrue;
	}

	// I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else
	if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_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->flags &= ~FL_BOUNCE_SHRAPNEL;
		}
		return;
	}

	if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber)
	{ //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 (!isKnockedSaber)
	{
		if (other->takedamage && other->client && other->client->ps.duelInProgress &&
			other->client->ps.duelIndex != ent->r.ownerNum)
		{
			goto killProj;
		}
	}

	if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY)
	{
		if (ent->methodOfDeath != MOD_REPEATER_ALT &&
			ent->methodOfDeath != MOD_ROCKET &&
			ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
			ent->methodOfDeath != MOD_ROCKET_HOMING &&
			ent->methodOfDeath != MOD_THERMAL &&
			ent->methodOfDeath != MOD_THERMAL_SPLASH &&
			ent->methodOfDeath != MOD_TRIP_MINE_SPLASH &&
			ent->methodOfDeath != MOD_TIMED_MINE_SPLASH &&
			ent->methodOfDeath != MOD_DET_PACK_SPLASH &&
			ent->methodOfDeath != MOD_VEHICLE &&
			ent->methodOfDeath != MOD_CONC &&
			ent->methodOfDeath != MOD_CONC_ALT &&
			ent->methodOfDeath != MOD_SABER &&
			ent->methodOfDeath != MOD_TURBLAST)
		{
			vec3_t fwd;

			if (trace)
			{
				VectorCopy(trace->plane.normal, fwd);
			}
			else
			{ //oh well
				AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
			}

			G_DeflectMissile(other, ent, fwd);
			G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
			return;
		}
	}

	if ((other->flags & FL_SHIELDED) &&
		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->s.weapon != WP_EMPLACED_GUN &&
		ent->methodOfDeath != MOD_REPEATER_ALT &&
		ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && 
		ent->methodOfDeath != MOD_TURBLAST &&
		ent->methodOfDeath != MOD_VEHICLE &&
		ent->methodOfDeath != MOD_CONC &&
		ent->methodOfDeath != MOD_CONC_ALT &&
		!(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) )
	{
		vec3_t fwd;

		if (other->client)
		{
			AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL);
		}
		else
		{
			AngleVectors(other->r.currentAngles, fwd, NULL, NULL);
		}

		G_DeflectMissile(other, ent, fwd);
		G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd);
		return;
	}

	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 &&
		ent->methodOfDeath != MOD_REPEATER_ALT &&
		ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH &&
		ent->methodOfDeath != MOD_CONC &&
		ent->methodOfDeath != MOD_CONC_ALT &&
		other->client->ps.saberBlockTime < level.time &&
		!isKnockedSaber &&
		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_SABER_DEFENSE];

		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;
		te->s.weapon = 0;//saberNum
		te->s.legsAnim = 0;//bladeNum

		/*if (other->client->ps.velocity[2] > 0 ||
			other->client->pers.cmd.forwardmove ||
			other->client->pers.cmd.rightmove)
			*/
		if (other->client->ps.velocity[2] > 0 ||
			other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge.
		{
			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, g_randomConeReflection.integer & CONE_REFLECT_SDEF);
		}
		other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); 

		//For jedi AI
		other->client->ps.saberEventFlags |= SEF_DEFLECTED;

		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) && !isKnockedSaber)
	{ //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 &&
			ent->methodOfDeath != MOD_CONC &&
			ent->methodOfDeath != MOD_CONC_ALT )
		{ //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_SABER_DEFENSE];

			//in this case, deflect it even if we can't actually block it because it hit our saber
			if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0)
			{
				WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue);
			}

			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;
			te->s.weapon = 0;//saberNum
			te->s.legsAnim = 0;//bladeNum

			if (otherOwner->client->ps.velocity[2] > 0 ||
				otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge.
			{
				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, g_randomConeReflection.integer & CONE_REFLECT_SDEF);
			}
			otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100));

			//For jedi AI
			otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED;

			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 && !isKnockedSaber) {
		// FIXME: wrong damage direction?
		if ( ent->damage ) {
			vec3_t	velocity;
			qboolean didDmg = qfalse;

			if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) &&
				!ent->isReflected) {
				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)) 
				{
					/* fix: there are rare situations where flechette did
					explode by timeout AND by impact in the very same frame, then here
					ent->think was set to G_FreeEntity, so the folowing think 
					did invalidate this entity, BUT it would be reused later in this 
					function for explosion event. This, then, would set ent->freeAfterEvent
					to qtrue, so event later, when reusing this entity by using G_InitEntity(),
					it would have this freeAfterEvent set AND this would in case of dropped
					item erase it from game immeadiately. THIS for example caused
					very rare flag dissappearing bug.	 */
					if (ent->think == WP_flechette_alt_blow)
						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);
					didDmg = qtrue;
				}
			}
			else
			{
				G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
					/*ent->s.origin*/ent->r.currentOrigin, ent->damage, 
					0, ent->methodOfDeath);
				didDmg = qtrue;
			}

			if (didDmg && other && other->client)
			{ //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever.
				class_t	npc_class = other->client->NPC_class;

				// If we are a robot and we aren't currently doing the full body electricity...
				if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE ||
					   npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE ||
					   npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd
					   npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY )
				{
					// special droid only behaviors
					if ( other->client->ps.electrifyTime < level.time + 100 )
					{
						// ... do the effect for a split second for some more feedback
						other->client->ps.electrifyTime = level.time + 450;
					}
					//FIXME: throw some sparks off droids,too
				}
			}
		}

		if ( ent->s.weapon == WP_DEMP2 )
		{//a hit with demp2 decloaks people, disables ships
			if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE )
			{//hit a vehicle
				if ( other->m_pVehicle //valid vehicle ent
					&& other->m_pVehicle->m_pVehicleInfo//valid stats
					&& (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders
						||(other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp("vehicle_proj", ent->classname ) == 0) )//only vehicle ion weapons affect a fighter in this manner
					&& !FighterIsLanded( other->m_pVehicle , &other->client->ps )//not landed
					&& !(other->spawnflags&2) )//and not suspended
				{//vehicles hit by "ion cannons" lose control
					if ( other->client->ps.electrifyTime > level.time )
					{//add onto it
						//FIXME: extern the length of the "out of control" time?
						other->client->ps.electrifyTime += Q_irand(200,500);
						if ( other->client->ps.electrifyTime > level.time + 4000 )
						{//cap it
							other->client->ps.electrifyTime = level.time + 4000;
						}
					}
					else
					{//start it
						//FIXME: extern the length of the "out of control" time?
						other->client->ps.electrifyTime = level.time + Q_irand(200,500);
					}
				}
			}
			else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] )
			{
				Jedi_Decloak( other );
				if ( ent->methodOfDeath == MOD_DEMP2_ALT )
				{//direct hit with alt disables cloak forever
					//permanently disable the saboteur's cloak
					other->client->cloakToggleTime = Q3_INFINITE;
				}
				else
				{//temp disable
					other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 );
				}
			}
		}
	}

killProj:

	if (!ent->inuse){
		G_LogPrintf("ERROR: entity %i non-used, checkpoint 5\n",ent-g_entities);
	}

	// 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 && !isKnockedSaber ) {
		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 && !isKnockedSaber) {
		G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) );
	}

	if (!isKnockedSaber)
	{
		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 );

	ent->takedamage = qfalse;
	// 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, ent->splashMethodOfDeath ) ) {
			if( !hitClient 
				&& g_entities[ent->r.ownerNum].client 
				&& !ent->isReflected) {
				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 );
}
Esempio n. 30
0
/*
==================
CG_AddMoveScale
==================
*/
static void CG_AddMoveScale( localEntity_t *le ) {
	refEntity_t	*re;
	vec3_t		delta;
	float		len;

	re = &le->refEntity;

	if ( !( le->leFlags & (LEF_DONT_SCALE | LEF_ALT_SCALE) ) ) {
		re->radius = le->radius * ( cg.time - le->startTime ) * le->lifeRate + 8;
	}
	if ( le->leFlags & LEF_ALT_SCALE ) {
		if ( re->hModel ) {
			if ( (le->leFlags & LEF_AXIS_ALIGNED) && le->radius ) {
				// it has a variing scale
				AxisClear( re->axis );
				AxisScale( re->axis, re->radius + le->radius * (cg.time - le->startTime) / (le->endTime - le->startTime), re->axis );
			}
			if ( !(le->leFlags & LEF_AXIS_ALIGNED) ) {
				// model looks in flight direction
				vec3_t	delta, angles;
				
				BG_EvaluateTrajectoryDelta( &le->pos, cg.time, delta );
				vectoangles( delta, angles );
				AnglesToAxis( angles, re->axis );
				if ( re->radius || le->radius ) AxisScale( re->axis, re->radius + le->radius * (cg.time - le->startTime) / (le->endTime - le->startTime), re->axis );
			}
		} else {
			// sprites scale like this
			re->radius += le->radius * cg.frametime / (le->endTime - le->startTime);
		}
	}

	BG_EvaluateTrajectory( &le->pos, cg.time, re->origin );

	// check for collisions
	if ( le->leFlags & LEF_COLLISIONS ) {
		// do a trace sometimes
		if ( le->ti.trailTime++ > 5 ) {
			trace_t		trace;

			le->ti.trailTime = 0;

			CG_Trace( &trace, re->oldorigin, NULL, NULL, re->origin, le->owner, MASK_SHOT );
			VectorCopy( re->origin, re->oldorigin );

			// hit something
			if ( trace.fraction < 1.0 ) {
				entityState_t	*s1;
				int		life;

				// do impact effect
				s1 = &cg_entities[le->owner].currentState;
				life = s1->groundEntityNum;

				if ( !( trace.surfaceFlags & SURF_NOIMPACT ) ) {
					if ( s1->generic1 & PF_IMPACT_MODEL ) {
						CG_MakeExplosion( trace.endpos, trace.plane.normal, 
								   cgs.gameModels[s1->modelindex2],	cgs.gameShaders[s1->otherEntityNum2],
								   life, qfalse, qfalse );
					} else if ( s1->modelindex2 ) {
						CG_MakeExplosion( trace.endpos, trace.plane.normal, 
								   cgs.media.dishFlashModel, cgs.gameShaders[s1->modelindex2],
								   life, qtrue, qtrue );
					}
				}

				CG_FreeLocalEntity( le );
				return;
			}
		}
	}

	// if the view would be "inside" the sprite, kill the sprite
	// so it doesn't add too much overdraw
	VectorSubtract( re->origin, cg.refdef.vieworg, delta );
	len = VectorLength( delta );
	if ( len < le->radius ) {
		CG_FreeLocalEntity( le );
		return;
	}

	trap_R_AddRefEntityToScene( re );
}