예제 #1
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 );
}
예제 #2
0
파일: g_missile.cpp 프로젝트: BSzili/OpenJK
//------------------------------------------------------
void G_MissileImpact( gentity_t *ent, trace_t *trace, int hitLoc=HL_NONE ) 
{
	gentity_t		*other;
	vec3_t			diff;

	other = &g_entities[trace->entityNum];
	if ( other == ent )
	{
		assert(0&&"missile hit itself!!!");
		return;
	}
	if ( trace->plane.normal[0] == 0.0f &&
		 trace->plane.normal[1] == 0.0f &&
		 trace->plane.normal[2] == 0.0f
		)
	{//model moved into missile in flight probably...
		trace->plane.normal[0] = -ent->s.pos.trDelta[0];
		trace->plane.normal[1] = -ent->s.pos.trDelta[1];
		trace->plane.normal[2] = -ent->s.pos.trDelta[2];
		VectorNormalize(trace->plane.normal);
	}

	if ( ent->owner && (other->takedamage||other->client) )
	{
		if ( !ent->lastEnemy || ent->lastEnemy == ent->owner )
		{//a missile that was not reflected or, if so, still is owned by original owner
			if( LogAccuracyHit( other, ent->owner ) ) 
			{
				ent->owner->client->ps.persistant[PERS_ACCURACY_HITS]++;
			}
			if ( ent->owner->client && !ent->owner->s.number )
			{
				if ( W_AccuracyLoggableWeapon( ent->s.weapon, qfalse, ent->methodOfDeath ) )
				{
					ent->owner->client->sess.missionStats.hits++;
				}
			}
		}
	}
	// check for bounce
	//OR: if the surfaceParm is has a reflect property (magnetic shielding) and the missile isn't an exploding missile
	qboolean bounce = !!( (!other->takedamage && (ent->s.eFlags&(EF_BOUNCE|EF_BOUNCE_HALF))) || (((trace->surfaceFlags&SURF_FORCEFIELD)||(other->flags&FL_SHIELDED))&&!ent->splashDamage&&!ent->splashRadius&&ent->s.weapon != WP_NOGHRI_STICK) );

	if ( ent->dflags & DAMAGE_HEAVY_WEAP_CLASS )
	{
		// heavy class missiles generally never bounce.
		bounce = qfalse;
	}

	if ( other->flags & (FL_DMG_BY_HEAVY_WEAP_ONLY | FL_SHIELDED ))
	{
		// Dumb assumption, but I guess we must be a shielded ion_cannon??  We should probably verify
		// if it's an ion_cannon that's Heavy Weapon only, we don't want to make it shielded do we...?
		if ( (!strcmp( "misc_ion_cannon", other->classname )) && (other->flags & FL_SHIELDED) )
		{
			// Anything will bounce off of us.
			bounce = qtrue;

			// Not exactly the debounce time, but rather the impact time for the shield effect...play effect for 1 second
			other->painDebounceTime = level.time + 1000;
		}
	}

	if ( ent->s.weapon == WP_DEMP2 )
	{
		// demp2 shots can never bounce
		bounce = qfalse;

		// in fact, alt-charge shots will not call the regular impact functions
		if ( ent->alt_fire )
		{
			// detonate at the trace end
			VectorCopy( trace->endpos, ent->currentOrigin );
			VectorCopy( trace->plane.normal, ent->pos1 );
			DEMP2_AltDetonate( ent );
			return;
		}
	}

	if ( bounce ) 
	{
		// Check to see if there is a bounce count
		if ( ent->bounceCount ) 
		{
			// decrement number of bounces and then see if it should be done bouncing
			if ( !(--ent->bounceCount) ) {
				// He (or she) will bounce no more (after this current bounce, that is).
				ent->s.eFlags &= ~( EF_BOUNCE | EF_BOUNCE_HALF );
			}
		}

		if ( other->NPC )
		{
			G_Damage( other, ent, ent->owner, ent->currentOrigin, ent->s.pos.trDelta, 0, DAMAGE_NO_DAMAGE, MOD_UNKNOWN );
		}

		G_BounceMissile( ent, trace );
		
		if ( ent->owner )//&& ent->owner->s.number == 0 ) 
		{
			G_MissileAddAlerts( ent );
		}
		G_MissileBounceEffect( ent, trace->endpos, trace->plane.normal, trace->entityNum==ENTITYNUM_WORLD );

		return;
	}
	
	// I would glom onto the EF_BOUNCE code section above, but don't feel like risking breaking something else
	if ( (!other->takedamage && ( ent->s.eFlags&(EF_BOUNCE_SHRAPNEL) ) ) 
		|| ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius) ) 
	{
		if ( !(other->contents&CONTENTS_LIGHTSABER)
			|| g_spskill->integer <= 0//on easy, it reflects all shots
			|| (g_spskill->integer == 1 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 )//on medium it won't reflect flechette or demp shots
			|| (g_spskill->integer >= 2 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_BOWCASTER && ent->s.weapon != WP_REPEATER )//on hard it won't reflect flechette, demp, repeater or bowcaster shots
			)
		{
			G_BounceMissile( ent, trace );

			if ( --ent->bounceCount < 0 )
			{
				ent->s.eFlags &= ~EF_BOUNCE_SHRAPNEL;
			}
			G_MissileBounceEffect( ent, trace->endpos, trace->plane.normal, trace->entityNum==ENTITYNUM_WORLD );
			return;
		}
	}

	if ( (!other->takedamage || (other->client && other->health <= 0))
		&& 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;
	}

	// check for sticking
	if ( ent->s.eFlags & EF_MISSILE_STICK ) 
	{
		if ( ent->owner )//&& ent->owner->s.number == 0 ) 
		{
			//Add the event
			if ( ent->s.weapon == WP_TRIP_MINE )
			{
				AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius/2, AEL_DISCOVERED/*AEL_DANGER*/, qfalse, qtrue );
				AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DISCOVERED/*AEL_DANGER*/, 60 );
				/*
				AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER, qfalse, qtrue );
				AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER, 60 );
				*/
			}
			else
			{
				AddSoundEvent( ent->owner, ent->currentOrigin, 128, AEL_DISCOVERED, qfalse, qtrue );
				AddSightEvent( ent->owner, ent->currentOrigin, 256, AEL_DISCOVERED, 10 );
			}
		}

		G_MissileStick( ent, other, trace );
		return;
	}

extern bool WP_DoingMoronicForcedAnimationForForcePowers(gentity_t *ent);
	// check for hitting a lightsaber
	if ( other->contents & CONTENTS_LIGHTSABER )
	{
		if ( other->owner && !other->owner->s.number && other->owner->client )
		{
			other->owner->client->sess.missionStats.saberBlocksCnt++;
		}
		if ( ( g_spskill->integer <= 0//on easy, it reflects all shots
				|| (g_spskill->integer == 1 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 )//on medium it won't reflect flechette or demp shots
				|| (g_spskill->integer >= 2 && ent->s.weapon != WP_FLECHETTE && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_BOWCASTER && ent->s.weapon != WP_REPEATER )//on hard it won't reflect flechette, demp, repeater or bowcaster shots
			 )
			&& (!ent->splashDamage || !ent->splashRadius) //this would be cool, though, to "bat" the thermal det away...
			&& ent->s.weapon != WP_NOGHRI_STICK )//gas bomb, don't reflect
		{	
			//FIXME: take other's owner's FP_SABER_DEFENSE into account here somehow?
			if (  !other->owner || !other->owner->client || other->owner->client->ps.saberInFlight 
				|| (InFront( ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE ) &&
				!WP_DoingMoronicForcedAnimationForForcePowers(other)) )//other->owner->s.number != 0 || 
			{//Jedi cannot block shots from behind!
				int blockChance = 0;
				switch ( other->owner->client->ps.forcePowerLevel[FP_SABER_DEFENSE] )
				{//level 1 reflects 50% of the time, level 2 reflects 75% of the time
				case FORCE_LEVEL_3:
					blockChance = 10;
					break;
				case FORCE_LEVEL_2:
					blockChance = 3;
					break;
				case FORCE_LEVEL_1:
					blockChance = 1;
					break;
				}
				if ( blockChance && (other->owner->client->ps.forcePowersActive&(1<<FP_SPEED)) )
				{//in in force speed, better chance of deflecting the shot
					blockChance += other->owner->client->ps.forcePowerLevel[FP_SPEED]*2;
				}
				if ( Q_irand( 0, blockChance ) )
				{
					VectorSubtract(ent->currentOrigin, other->currentOrigin, diff);
					VectorNormalize(diff);
					G_ReflectMissile( other, ent, diff);
					if ( other->owner && other->owner->client )
					{
						other->owner->client->ps.saberEventFlags |= SEF_DEFLECTED;
					}
					//do the effect
					VectorCopy( ent->s.pos.trDelta, diff );
					VectorNormalize( diff );
					G_MissileReflectEffect( ent, trace->endpos, trace->plane.normal );
					return;
				}
			}
		}
		else
		{//still do the bounce effect
			G_MissileReflectEffect( ent, trace->endpos, trace->plane.normal );
		}
	}

	G_MissileImpacted( ent, other, trace->endpos, trace->plane.normal, hitLoc );
}
예제 #3
0
파일: g_missile.c 프로젝트: videoP/jaPRO
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 );
}
예제 #4
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 );
}