コード例 #1
0
ファイル: g_missile.cpp プロジェクト: Arcadiaprime/japp
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.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 );
}
コード例 #3
0
ファイル: g_missile.c プロジェクト: dpadgett/base_enhanced
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 );
}
コード例 #4
0
ファイル: bg_slidemove.c プロジェクト: Chedo/OpenJK
void PM_VehicleImpact(bgEntity_t *pEnt, trace_t *trace)
{
	// See if the vehicle has crashed into the ground.
	Vehicle_t *pSelfVeh = pEnt->m_pVehicle;
	float magnitude = VectorLength( pm->ps->velocity ) * pSelfVeh->m_pVehicleInfo->mass / 50.0f;
	qboolean forceSurfDestruction = qfalse;
#ifdef QAGAME
	gentity_t *hitEnt = trace!=NULL?&g_entities[trace->entityNum]:NULL;

	if (!hitEnt || //nothing to hit
		(pSelfVeh && pSelfVeh->m_pPilot &&//I'm a piloted vehicle
		hitEnt && hitEnt->s.eType == ET_MISSILE && hitEnt->inuse &&//that hit a missile
		hitEnt->r.ownerNum == pSelfVeh->m_pPilot->s.number)//and the missile is owned by my pilot
		)
	{//don't hit it
		return;
	}

	if ( pSelfVeh//I have a vehicle struct
		&& pSelfVeh->m_iRemovedSurfaces )//vehicle has bits removed
	{//spiralling to our deaths, explode on any solid impact
		if ( hitEnt->s.NPC_class == CLASS_VEHICLE )
		{//hit another vehicle, explode!
			//Give credit to whoever got me into this death spiral state
			G_DamageFromKiller( (gentity_t *)pEnt, (gentity_t *)pSelfVeh->m_pParentEntity, (gentity_t *)hitEnt, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_COLLISION );
			return;
		}
		else if ( !VectorCompare( trace->plane.normal, vec3_origin )
			&& (trace->entityNum == ENTITYNUM_WORLD || hitEnt->r.bmodel ) )
		{//have a valid hit plane and we hit a solid brush
			vec3_t	moveDir;
			float	impactDot;
			VectorCopy( pm->ps->velocity, moveDir );
			VectorNormalize( moveDir );
			impactDot = DotProduct( moveDir, trace->plane.normal );
			if ( impactDot <= -0.7f )//hit rather head-on and hard
			{// Just DIE now
				//Give credit to whoever got me into this death spiral state
				G_DamageFromKiller( (gentity_t *)pEnt, (gentity_t *)pSelfVeh->m_pParentEntity, (gentity_t *)hitEnt, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );
				return;
			}
		}
	}
	
	if ( trace->entityNum < ENTITYNUM_WORLD
		&& hitEnt->s.eType == ET_MOVER
		&& hitEnt->s.apos.trType != TR_STATIONARY//rotating
		&& (hitEnt->spawnflags&16) //IMPACT
		&& Q_stricmp( "func_rotating", hitEnt->classname ) == 0 )
	{//hit a func_rotating that is supposed to destroy anything it touches!
		//guarantee the hit will happen, thereby taking off a piece of the ship
		forceSurfDestruction = qtrue;
	}
	else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f
		&& pm->ps->velocity[2] > -100.0f )
#else
	if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f
		&& pm->ps->velocity[2] > -100.0f )
#endif
		/*
	if ( (pSelfVeh->m_ulFlags&VEH_GEARSOPEN) 
		&& trace->plane.normal[2] > 0.7f
		&& fabs(pSelfVeh->m_vOrientation[PITCH]) < 0.2f
		&& fabs(pSelfVeh->m_vOrientation[ROLL]) < 0.2f )*/
	{//we're landing, we're cool
		//FIXME: some sort of landing "thump", not the impactFX
		/*
		if ( pSelfVeh->m_pVehicleInfo->iImpactFX )
		{
			vec3_t up = {0,0,1};
#ifdef QAGAME
			G_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, up );
#else
			trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, up, -1, -1 );
#endif
		}
		*/
		//this was annoying me -rww
		//FIXME: this shouldn't even be getting called when the vehicle is at rest!
#ifdef QAGAME
		if (hitEnt && (hitEnt->s.eType == ET_PLAYER || hitEnt->s.eType == ET_NPC) && pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)
		{ //always smack players
		}
		else
#endif
		{
			return;
		}
	}
	if ( pSelfVeh &&
		(pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) && //this is kind of weird on tauntauns and atst's..
		(magnitude >= 100||forceSurfDestruction) )
	{
		if ( pEnt->m_pVehicle->m_iHitDebounce < pm->cmd.serverTime 
			|| forceSurfDestruction )
		{//a bit of a hack, may conflict with getting shot, but...
			//FIXME: impact sound and effect should be gotten from g_vehicleInfo...?
			//FIXME: should pass in trace.endpos and trace.plane.normal
			vec3_t	vehUp;
#ifdef QAGAME
			qboolean noDamage = qfalse;
#else
			bgEntity_t *hitEnt;
#endif

			if ( trace && !pSelfVeh->m_iRemovedSurfaces && !forceSurfDestruction )
			{
				qboolean turnFromImpact = qfalse, turnHitEnt = qfalse;
				float l = pm->ps->speed*0.5f;
				vec3_t	bounceDir;
#ifndef QAGAME
				bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum);
#endif
				if ( (trace->entityNum == ENTITYNUM_WORLD || hitEnt->s.solid == SOLID_BMODEL)//bounce off any brush
					 && !VectorCompare(trace->plane.normal, vec3_origin) )//have a valid plane to bounce off of
				{ //bounce off in the opposite direction of the impact
					if (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER)
					{
						pm->ps->speed *= pml.frametime;
						VectorCopy(trace->plane.normal, bounceDir);
					}
					else if ( trace->plane.normal[2] >= MIN_LANDING_SLOPE//flat enough to land on
						&& pSelfVeh->m_LandTrace.fraction < 1.0f //ground present
						&& pm->ps->speed <= MIN_LANDING_SPEED )
					{//could land here, don't bounce off, in fact, return altogether!
						return;
					}
					else
					{
						if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)
						{
							turnFromImpact = qtrue;
						}
						VectorCopy(trace->plane.normal, bounceDir);
					}
				}
				else if ( pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER )
				{//check for impact with another fighter
#ifndef QAGAME
					bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum);
#endif
					if ( hitEnt->s.NPC_class == CLASS_VEHICLE
						&& hitEnt->m_pVehicle 
						&& hitEnt->m_pVehicle->m_pVehicleInfo
						&& hitEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER )
					{//two vehicles hit each other, turn away from the impact
						turnFromImpact = qtrue;
						turnHitEnt = qtrue;
#ifndef QAGAME
						VectorSubtract( pm->ps->origin, hitEnt->s.origin, bounceDir );
#else
						VectorSubtract( pm->ps->origin, hitEnt->r.currentOrigin, bounceDir );
#endif
						VectorNormalize( bounceDir );
					}
				}
				if ( turnFromImpact )
				{//bounce off impact surf and turn away
					vec3_t	pushDir={0}, turnAwayAngles, turnDelta;
					float	turnStrength, pitchTurnStrength, yawTurnStrength;
					vec3_t	moveDir;
					float bounceDot, turnDivider;
					//bounce
					if ( !turnHitEnt )
					{//hit wall
						VectorScale(bounceDir, (pm->ps->speed*0.25f/pSelfVeh->m_pVehicleInfo->mass), pushDir);
					}
					else
					{//hit another fighter
#ifndef QAGAME
						VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, bounceDir );
#else
						if ( hitEnt->client )
						{
							VectorScale( bounceDir, (pm->ps->speed+hitEnt->client->ps.speed)*0.5f, pushDir );
						}
						else
						{
							VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, pushDir );
						}
#endif
						VectorScale(pushDir, (l/pSelfVeh->m_pVehicleInfo->mass), pushDir);
						VectorScale(pushDir, 0.1f, pushDir);
					}
					VectorNormalize2( pm->ps->velocity, moveDir );
					bounceDot = DotProduct( moveDir, bounceDir )*-1;
					if ( bounceDot < 0.1f )
					{
						bounceDot = 0.1f;
					}
					VectorScale( pushDir, bounceDot, pushDir );
					VectorAdd(pm->ps->velocity, pushDir, pm->ps->velocity);
					//turn
					turnDivider = (pSelfVeh->m_pVehicleInfo->mass/400.0f);
					if ( turnHitEnt )
					{//don't turn as much when hit another ship
						turnDivider *= 4.0f;
					}
					if ( turnDivider < 0.5f )
					{
						turnDivider = 0.5f;
					}
					turnStrength = (magnitude/2000.0f);
					if ( turnStrength < 0.1f )
					{
						turnStrength = 0.1f;
					}
					else if ( turnStrength > 2.0f )
					{
						turnStrength = 2.0f;
					}
					//get the angles we are going to turn towards
					vectoangles( bounceDir, turnAwayAngles );
					//get the delta from our current angles to those new angles
					AnglesSubtract( turnAwayAngles, pSelfVeh->m_vOrientation, turnDelta );
					//now do pitch
					if ( !bounceDir[2] )
					{//shouldn't be any pitch
					}
					else
					{
						pitchTurnStrength = turnStrength*turnDelta[PITCH];
						if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE )
						{
							pitchTurnStrength = MAX_IMPACT_TURN_ANGLE;
						}
						else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE )
						{
							pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE;
						}
						//pSelfVeh->m_vOrientation[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier);
						pSelfVeh->m_vFullAngleVelocity[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier);
					}
					//now do yaw
					if ( !bounceDir[0] 
						&& !bounceDir[1] )
					{//shouldn't be any yaw
					}
					else
					{
						yawTurnStrength = turnStrength*turnDelta[YAW];
						if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE )
						{
							yawTurnStrength = MAX_IMPACT_TURN_ANGLE;
						}
						else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE )
						{
							yawTurnStrength = -MAX_IMPACT_TURN_ANGLE;
						}
						//pSelfVeh->m_vOrientation[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier);
						pSelfVeh->m_vFullAngleVelocity[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier);
					}
					/*
					PM_SetPMViewAngle(pm->ps, pSelfVeh->m_vOrientation, &pSelfVeh->m_ucmd);
					if ( pm_entVeh )
					{//I'm a vehicle, so pm_entVeh is actually my pilot
						bgEntity_t *pilot = pm_entVeh;
						if ( !BG_UnrestrainedPitchRoll( pilot->playerState, pSelfVeh ) )
						{
							//set the rider's viewangles to the vehicle's viewangles
							PM_SetPMViewAngle(pilot->playerState, pSelfVeh->m_vOrientation, &pSelfVeh->m_ucmd);
						}
					}
					*/
#ifdef QAGAME//server-side, turn the guy we hit away from us, too
					if ( turnHitEnt//make the other guy turn and get pushed
						&& hitEnt->client //must be a valid client
						&& !FighterIsLanded( hitEnt->m_pVehicle, &hitEnt->client->ps )//but not if landed
						&& !(hitEnt->spawnflags&2) )//and not if suspended
					{
						l = hitEnt->client->ps.speed;
						//now bounce *them* away and turn them
						//flip the bounceDir
						VectorScale( bounceDir, -1, bounceDir );
						//do bounce
						VectorScale( bounceDir, (pm->ps->speed+l)*0.5f, pushDir );
						VectorScale(pushDir, (l*0.5f/hitEnt->m_pVehicle->m_pVehicleInfo->mass), pushDir);
						VectorNormalize2( hitEnt->client->ps.velocity, moveDir );
						bounceDot = DotProduct( moveDir, bounceDir )*-1;
						if ( bounceDot < 0.1f )
						{
							bounceDot = 0.1f;
						}
						VectorScale( pushDir, bounceDot, pushDir );
						VectorAdd(hitEnt->client->ps.velocity, pushDir, hitEnt->client->ps.velocity);
						//turn
						turnDivider = (hitEnt->m_pVehicle->m_pVehicleInfo->mass/400.0f);
						if ( turnHitEnt )
						{//don't turn as much when hit another ship
							turnDivider *= 4.0f;
						}
						if ( turnDivider < 0.5f )
						{
							turnDivider = 0.5f;
						}
						//get the angles we are going to turn towards
						vectoangles( bounceDir, turnAwayAngles );
						//get the delta from our current angles to those new angles
						AnglesSubtract( turnAwayAngles, hitEnt->m_pVehicle->m_vOrientation, turnDelta );
						//now do pitch
						if ( !bounceDir[2] )
						{//shouldn't be any pitch
						}
						else
						{
							pitchTurnStrength = turnStrength*turnDelta[PITCH];
							if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE )
							{
								pitchTurnStrength = MAX_IMPACT_TURN_ANGLE;
							}
							else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE )
							{
								pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE;
							}
							//hitEnt->m_pVehicle->m_vOrientation[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier);
							hitEnt->m_pVehicle->m_vFullAngleVelocity[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier);
						}
						//now do yaw
						if ( !bounceDir[0] 
							&& !bounceDir[1] )
						{//shouldn't be any yaw
						}
						else
						{
							yawTurnStrength = turnStrength*turnDelta[YAW];
							if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE )
							{
								yawTurnStrength = MAX_IMPACT_TURN_ANGLE;
							}
							else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE )
							{
								yawTurnStrength = -MAX_IMPACT_TURN_ANGLE;
							}
							//hitEnt->m_pVehicle->m_vOrientation[ROLL] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier);
							hitEnt->m_pVehicle->m_vFullAngleVelocity[ROLL] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier);
						}
						//NOTE: will these angle changes stick or will they be stomped 
						//		when the vehicle goes through its own update and re-grabs 
						//		its angles from its pilot...?  Should we do a 
						//		SetClientViewAngles on the pilot?
						/*
						SetClientViewAngle( hitEnt, hitEnt->m_pVehicle->m_vOrientation );
						if ( hitEnt->m_pVehicle->m_pPilot 
							&& ((gentity_t *)hitEnt->m_pVehicle->m_pPilot)->client )
						{
							SetClientViewAngle( (gentity_t *)hitEnt->m_pVehicle->m_pPilot, hitEnt->m_pVehicle->m_vOrientation );
						}
						*/
					}
#endif
				}
			}

#ifdef QAGAME
			if (!hitEnt)
			{
				return;
			}

			AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp );
			if ( pSelfVeh->m_pVehicleInfo->iImpactFX )
			{
				//G_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp );
				//tempent use bad!
				G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pSelfVeh->m_pVehicleInfo->iImpactFX);
			}
			pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200;
			magnitude /= pSelfVeh->m_pVehicleInfo->toughness * 50.0f; 

			if (hitEnt 
				&& (hitEnt->s.eType != ET_TERRAIN || !(hitEnt->spawnflags & 1) || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER))
			{ //don't damage the vehicle from terrain that doesn't want to damage vehicles
				gentity_t *killer = NULL;
				if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)
				{ //increase the damage...
					float mult = (pSelfVeh->m_vOrientation[PITCH]*0.1f);
					if (mult < 1.0f)
					{
						mult = 1.0f;
					}
					if (hitEnt->inuse && hitEnt->takedamage)
					{ //if the other guy takes damage, don't hurt us a lot for ramming him
						//unless it's a vehicle, then we get 1.5 times damage
						if (hitEnt->s.eType == ET_NPC &&
							hitEnt->s.NPC_class == CLASS_VEHICLE &&
							hitEnt->m_pVehicle)
						{
							mult = 1.5f;
						}
						else
						{
							mult = 0.5f;
						}
					}

					magnitude *= mult;
				}
				pSelfVeh->m_iLastImpactDmg = magnitude;
				//FIXME: what about proper death credit to the guy who shot you down?
				//FIXME: actually damage part of the ship that impacted?
				if ( hitEnt->s.eType == ET_MISSILE )//missile
				{
					//FIX: NEVER do or take impact damage from a missile...
					noDamage = qtrue;
					if ( (hitEnt->s.eFlags&EF_JETPACK_ACTIVE)//vehicle missile
						&& ((gentity_t *)hitEnt)->r.ownerNum < MAX_CLIENTS )//valid client owner
					{//I ran into a missile and died because of the impact, give credit to the missile's owner (PROBLEM: might this ever accidently give improper credit to client 0?)
						/*
						if ( ((gentity_t *)hitEnt)->r.ownerNum == pEnt->s.number )
						{//hit our own missile?  Don't damage ourselves or it... (so we don't kill ourselves!) if it hits *us*, then fine, but not here
							noDamage = qtrue;
						}
						*/
						killer = &g_entities[((gentity_t *)hitEnt)->r.ownerNum];
					}
				}
				if ( !noDamage )
				{
					G_Damage( (gentity_t *)pEnt, ((gentity_t*)hitEnt), killer!=NULL?killer:((gentity_t *)hitEnt), NULL, pm->ps->origin, magnitude*5, DAMAGE_NO_ARMOR, (hitEnt->s.NPC_class==CLASS_VEHICLE?MOD_COLLISION:MOD_FALLING) );//FIXME: MOD_IMPACT
				}

				if (pSelfVeh->m_pVehicleInfo->surfDestruction)
				{
					G_FlyVehicleSurfaceDestruction((gentity_t *)pEnt, trace, magnitude, forceSurfDestruction );
				}

				pSelfVeh->m_ulFlags |= VEH_CRASHING;
			}

			if (hitEnt &&
				hitEnt->inuse &&
				hitEnt->takedamage)
			{ //damage this guy because we hit him
				float pmult = 1.0f;
				int finalD;
				gentity_t *attackEnt;

				if ( (hitEnt->s.eType == ET_PLAYER && hitEnt->s.number < MAX_CLIENTS) ||
					 (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class != CLASS_VEHICLE) )
				{ //probably a humanoid, or something
					if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)
					{ //player die good.. if me fighter
						pmult = 2000.0f;
					}
					else
					{
						pmult = 40.0f;
					}

					if (hitEnt->client &&
						BG_KnockDownable(&hitEnt->client->ps) &&
						G_CanBeEnemy((gentity_t *)pEnt, hitEnt))
					{ //smash!
						if (hitEnt->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN)
						{
							hitEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
							hitEnt->client->ps.forceHandExtendTime = pm->cmd.serverTime + 1100;
							hitEnt->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim
						}

						hitEnt->client->ps.otherKiller = pEnt->s.number;
						hitEnt->client->ps.otherKillerTime = pm->cmd.serverTime + 5000;
						hitEnt->client->ps.otherKillerDebounceTime = pm->cmd.serverTime + 100;
						hitEnt->client->otherKillerMOD = MOD_COLLISION;
						hitEnt->client->otherKillerVehWeapon = 0;
						hitEnt->client->otherKillerWeaponType = WP_NONE;

						//add my velocity into his to force him along in the correct direction from impact
						VectorAdd(hitEnt->client->ps.velocity, pm->ps->velocity, hitEnt->client->ps.velocity);
						//upward thrust
						hitEnt->client->ps.velocity[2] += 200.0f;
					}
				}

				if (pSelfVeh->m_pPilot)
				{
					attackEnt = (gentity_t *)pSelfVeh->m_pPilot;
				}
				else
				{
					attackEnt = (gentity_t *)pEnt;
				}

				finalD = magnitude*pmult;
				if (finalD < 1)
				{
					finalD = 1;
				}
				if ( !noDamage )
				{
					G_Damage( hitEnt, attackEnt, attackEnt, NULL, pm->ps->origin, finalD, 0, (hitEnt->s.NPC_class==CLASS_VEHICLE?MOD_COLLISION:MOD_FALLING/*MOD_MELEE*/) );//FIXME: MOD_IMPACT
				}
			}
#else	//this is gonna result in "double effects" for the client doing the prediction.
		//it doesn't look bad though. could just use predicted events, but I'm too lazy.
			hitEnt = PM_BGEntForNum(trace->entityNum);

			if (!hitEnt || hitEnt->s.owner != pEnt->s.number)
			{ //don't hit your own missiles!
				AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp );
				pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200;
				trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp, -1, -1 );

				pSelfVeh->m_ulFlags |= VEH_CRASHING;
			}
#endif
		}
	}
}
コード例 #5
0
ファイル: g_missile.c プロジェクト: jwginge/ojpa
//G_MissileImpact now returns qfalse if and only if the player physically dodged the damage.
//this allows G_RunMissile to properly handle he
qboolean G_MissileImpact( gentity_t *ent, trace_t *trace ) {
//void G_MissileImpact( gentity_t *ent, trace_t *trace ) {
//[/DodgeSys]
    gentity_t		*other;
    qboolean		isKnockedSaber = qfalse;
    //[DodgeSys]
    int missileDmg;
    //[/DodgeSys]

    other = &g_entities[trace->entityNum];

    // check for bounce
    //[WeaponSys]
    //allow thermals to bounce off players and such.
    if ( (!other->takedamage || ent->s.weapon == WP_THERMAL) &&
            //if ( !other->takedamage &&
            //[/WeaponSys]
            (ent->bounceCount > 0 || ent->bounceCount == -5) &&
            ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) {
        G_BounceMissile( ent, trace );
        G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 );
        //[DodgeSys]
        return qtrue;
        //return;
        //[/DodgeSys]
    }
    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 );
            //[DodgeSys]
            return qtrue;
            //return;
            //[/DodgeSys]
        }

        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;
        }
        //[DodgeSys]
        return qtrue;
        //return;
        //[/DodgeSys]
    }

    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 &&
                //[Asteroids]
                ent->methodOfDeath != MOD_TURBLAST &&
                ent->methodOfDeath != MOD_TARGET_LASER)
            //[/Asteroids]
        {
            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);
            //[DodgeSys]
            return qtrue;
            //return;
            //[/DodgeSys]
        }
    }

    //ROP VEHICLE_IMP START
    if((other->s.NPC_class == CLASS_VEHICLE && other->m_pVehicle
            && !other->m_pVehicle->m_pVehicleInfo->AllWeaponsDoDamageToShields
            && other->client->ps.stats[STAT_ARMOR] > 0) || other->flags & FL_SHIELDED)
    {
        if (ent->s.weapon != WP_ROCKET_LAUNCHER &&
                ent->s.weapon != WP_THERMAL &&
                ent->s.weapon != WP_GRENADE &&
                ent->s.weapon != WP_DET_PACK &&
                ent->s.weapon != WP_DEMP2 &&
                ent->s.weapon != WP_EMPLACED_GUN &&
                ent->s.weapon != WP_TURRET &&
                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);
            //[DodgeSys]
            return qtrue;
            //return;
            //[/DodgeSys]
        }
    }
    //ROP VEHICLE_IMP END

    //[BoltBlockSys]
    if (OJP_SaberCanBlock(other, ent, qfalse, trace->endpos, -1, -1))
        //[/BoltBlockSys]
    {   //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked)
        //[BoltBlockSys]
        //racc - missile hit the actual player and it's a type of missile that you can deflect/ref with the saber.

        //racc - play projectile block animation
        other->client->ps.weaponTime = 0;
        WP_SaberBlockNonRandom(other, ent->r.currentOrigin, qtrue);

        OJP_HandleBoltBlock(ent, other, trace);
        //[DodgeSys]
        return qtrue;
        //return;
        //[/DodgeSys]
        //[/BoltBlockSys]
    }
    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_TUSKEN_RIFLE &&
                ent->s.weapon != WP_ROCKET_LAUNCHER &&
                ent->s.weapon != WP_THERMAL &&
                ent->s.weapon != WP_GRENADE &&
                ent->s.weapon != WP_DET_PACK &&
                //[BoltBlockSys]
                //ent->s.weapon != WP_DEMP2 &&
                //[BoltBlockSys]
                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

            //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 && !BG_SaberInAttack(otherOwner->client->ps.saberMove))
                    || (otherOwner->client && (pm->cmd.buttons & BUTTON_FORCEPOWER || pm->cmd.buttons & BUTTON_FORCEGRIP
                                               || pm->cmd.buttons & BUTTON_FORCE_LIGHTNING) ))
            {   //racc - play projectile block animation even in .
                otherOwner->client->ps.weaponTime = 0;
                WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue);
            }

            //[BoltBlockSys]
            OJP_HandleBoltBlock(ent, otherOwner, trace);
            //[/BoltBlockSys]
            //[DodgeSys]
            return qtrue;
            //return;
            //[/DodgeSys]
        }
    }

    // check for sticking
    //[SaberThrowSys]
    if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK )
            && ent->s.weapon != WP_SABER)
        //if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) )
        //[/SaberThrowSys]
    {
        laserTrapStick( ent, trace->endpos, trace->plane.normal );
        G_AddEvent( ent, EV_MISSILE_STICK, 0 );
        //[DodgeSys]
        return qtrue;
        //return;
        //[/DodgeSys]
    }

    // impact damage
    if (other->takedamage && !isKnockedSaber) {
        //[DodgeSys]
        //make players be able to dodge projectiles.
        missileDmg = ent->damage;
        if(G_DoDodge(other, &g_entities[ent->r.ownerNum], trace->endpos, -1, &missileDmg, ent->methodOfDeath))
        {   //player dodged the damage, have missile continue moving.
            if(ent->s.weapon == WP_ROCKET_LAUNCHER)
                ent->genericValue1 = 0;
            return qfalse;
        }
        //[/DodgeSys]

        // FIXME: wrong damage direction?
        //[DodgeSys]
        if ( missileDmg ) {
            //if ( ent->damage ) {
            //[/DodgeSys]
            vec3_t	velocity;
            qboolean didDmg = qfalse;

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

            if (ent->s.weapon == WP_BOWCASTER || ent->s.weapon == WP_FLECHETTE ||
                    ent->s.weapon == WP_ROCKET_LAUNCHER)
            {
                if (ent->s.weapon == WP_FLECHETTE && (ent->s.eFlags & EF_ALT_FIRING))
                {
                    ent->think(ent);
                }
                else
                {
                    G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
                              //[DodgeSys]
                              ent->r.currentOrigin, missileDmg,
                              //[/DodgeSys]
                              DAMAGE_HALF_ABSORB, ent->methodOfDeath);
                    didDmg = qtrue;
                }
            }
            else {
                gentity_t *owner = &g_entities[ent->r.ownerNum];
                float distance = VectorDistance(owner->r.currentOrigin,other->r.currentOrigin);

                if(distance <= 100.0f)
                {
                    G_Damage (other, ent, owner, velocity,
                              //[DodgeSys]
                              ent->r.currentOrigin, missileDmg * 2,
                              //[/DodgeSys]
                              0, ent->methodOfDeath);
                }
                else if (distance <= 300.0f)
                {
                    G_Damage (other, ent, owner, velocity,
                              //[DodgeSys]
                              ent->r.currentOrigin, missileDmg * 1.5,
                              //[/DodgeSys]
                              0, ent->methodOfDeath);
                }
                else
                {
                    G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity,
                              //[DodgeSys]
                              ent->r.currentOrigin, missileDmg,
                              //[/DodgeSys]
                              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:
    // 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 ) {
        G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius,
                        other, ent, ent->splashMethodOfDeath );
    }

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

    trap_LinkEntity( ent );

    //[DodgeSys]
    return qtrue;
    //[/DodgeSys]
}