I got bullets to ricochet!

Puzzlemaker

Newbie
Joined
Aug 1, 2004
Messages
915
Reaction score
0
If anyone wants the code, just post here and I will give it to you. It was made by modifying the HL2 multiplayer code, and I havn't tested it with multiple people on the server at the same time, but it should work.

We should get an open-source mini section for the coding section.
 
Ricochet as in make a ricochet sound and graphic, or ricochet in the real sense of the word where you're calculating extra angles to make a second traceline?

-Angry Lawyer
 
Ricochet as in extra angles to make a second traceline.

Also, depending on how hard the object is and how bouncy it is, the bullet ricochets at diffrent angles. In other words, to ricochet off of grass, it has to be a very shallow angle compared to metal. It pretty much fires 2 bullets; one from the gun, and one from the ricochet point.

I will post the code as soon as I can, its fairly simple once I figured it out, and I stole some code from the crossbow bolt code.
 
You've really surprised me with how quickly you've learnt coding :) It was only two days ago you asked me the difference between client and server, and now you're producing stuff that I'd have a hard time figuring out.

-Angry Lawyer
 
He's the holy one :naughty: the one gifed in code, i though the matrix's had that copyrighted :O
 
Good job. Nicking code from the right places and making it work is half the trick as a beginning Source coder. Although I might be biased in that since it's how I did things at first :p, while experimenting with a hacked-together harpoon launcher weapon (I can recommend one of those, they're fun to test at least).
 
Here is the code. Its the firebullets function in baseentity_shared. Now all I have to do is put in some code to make it so bullets can go through things... Hmm.

Code:
void CBaseEntity::FireBullets( const FireBulletsInfo_t &info )
{
	static int	tracerCount;
	trace_t		tr;
	CAmmoDef*	pAmmoDef	= GetAmmoDef();
	int			nDamageType	= pAmmoDef->DamageType(info.m_iAmmoType);
	int			nAmmoFlags	= pAmmoDef->Flags(info.m_iAmmoType);
	
	bool bDoServerEffects = true;

#if defined( HL2MP ) && defined( GAME_DLL )
	bDoServerEffects = false;
#endif

	int iPlayerDamage = info.m_iPlayerDamage;
	if ( iPlayerDamage == 0 )
	{
		if ( nAmmoFlags & AMMO_INTERPRET_PLRDAMAGE_AS_DAMAGE_TO_PLAYER )
		{
			iPlayerDamage = pAmmoDef->PlrDamage( info.m_iAmmoType );
		}
	}

	// the default attacker is ourselves
	CBaseEntity *pAttacker = info.m_pAttacker ? info.m_pAttacker : this;

	// Make sure we don't have a dangling damage target from a recursive call
	if ( g_MultiDamage.GetTarget() != NULL )
	{
		ApplyMultiDamage();
	}
	  
	ClearMultiDamage();
	g_MultiDamage.SetDamageType( nDamageType | DMG_NEVERGIB );

	Vector vecDir;
	Vector vecEnd;
	
	CTraceFilterSkipTwoEntities traceFilter( this, info.m_pAdditionalIgnoreEnt, COLLISION_GROUP_NONE );

	bool bUnderwaterBullets = ShouldDrawUnderwaterBulletBubbles();
	bool bStartedInWater = false;
	bool bRicochetBullet = false;
	Vector vecTempDir;
	if ( bUnderwaterBullets )
	{
		bStartedInWater = ( enginetrace->GetPointContents( info.m_vecSrc ) & (CONTENTS_WATER|CONTENTS_SLIME) ) != 0;
	}

	// Prediction is only usable on players
	int iSeed = 0;
	if ( IsPlayer() )
	{
		iSeed = CBaseEntity::GetPredictionRandomSeed() & 255;
	}

#if defined( HL2MP ) && defined( GAME_DLL )
	int iEffectSeed = iSeed;
#endif
	//-----------------------------------------------------
	// Set up our shot manipulator.
	//-----------------------------------------------------
	CShotManipulator Manipulator( info.m_vecDirShooting );

	bool bDoImpacts = false;
	bool bDoTracers = false;
	
	for (int iShot = 0; iShot < info.m_iShots; iShot++)
	{
		bool bHitWater = false;
		bool bHitGlass = false;
		
		// Prediction is only usable on players
		if ( IsPlayer() )
		{
			RandomSeed( iSeed );	// init random system with this seed
		}

		// If we're firing multiple shots, and the first shot has to be bang on target, ignore spread
		if ( iShot == 0 && info.m_iShots > 1 && (info.m_nFlags & FIRE_BULLETS_FIRST_SHOT_ACCURATE) )
		{
			vecDir = Manipulator.GetShotDirection();
		}
		else
		{

			// Don't run the biasing code for the player at the moment.
			vecDir = Manipulator.ApplySpread( info.m_vecSpread );
		}

		// Make sure given a valid bullet type
		if (info.m_iAmmoType == -1)
		{
			DevMsg("ERROR: Undefined ammo type!\n");
			return;
		}
		//BUGBUG:  when a bullet it ricocheting, it should have a diffrent vecDir then 
		//the weapons spray.
		vecEnd = (((!bRicochetBullet) ? info.m_vecSrc : tr.endpos)) + ((!bRicochetBullet) ? vecDir : vecTempDir) * info.m_flDistance;

		if( IsPlayer() && info.m_iShots > 1 && iShot % 2 )
		{
			// Half of the shotgun pellets are hulls that make it easier to hit targets with the shotgun.
			AI_TraceHull( ((!bRicochetBullet) ? info.m_vecSrc : tr.endpos), vecEnd, Vector( -3, -3, -3 ), Vector( 3, 3, 3 ), MASK_SHOT, &traceFilter, &tr );
		}
		else
		{
			AI_TraceLine(((!bRicochetBullet) ? info.m_vecSrc : tr.endpos), vecEnd, MASK_SHOT, &traceFilter, &tr);
		}

#ifdef GAME_DLL
		if ( ai_debug_shoot_positions.GetBool() )
			NDebugOverlay::Line(((!bRicochetBullet) ? info.m_vecSrc : tr.endpos), vecEnd, 255, 255, 255, false, .1 );
#endif

		if ( bStartedInWater )
		{
#ifdef GAME_DLL
			CreateBubbleTrailTracer( ((!bRicochetBullet) ? info.m_vecSrc : tr.endpos), tr.endpos, vecDir );
#endif
			bHitWater = true;
		}

		Vector vecTracerDest = tr.endpos;

		// do damage, paint decals
		if (tr.fraction != 1.0)
		{
#ifdef GAME_DLL
			UpdateShotStatistics( tr );

			// For shots that don't need persistance
			int soundEntChannel = ( info.m_nFlags&FIRE_BULLETS_TEMPORARY_DANGER_SOUND ) ? SOUNDENT_CHANNEL_BULLET_IMPACT : SOUNDENT_CHANNEL_UNSPECIFIED;

			CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, tr.endpos, 200, 0.5, this, soundEntChannel );
#endif

			// See if the bullet ended up underwater + started out of the water
			if ( !bHitWater && ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) ) )
			{
				//BUGBUG:  Richocet bullets wont leave splashes!
				bHitWater = HandleShotImpactingWater( info, vecEnd, &traceFilter, &vecTracerDest );
			}

			float flActualDamage = info.m_iDamage;

			// If we hit a player, and we have player damage specified, use that instead
			// Adrian: Make sure to use the currect value if we hit a vehicle the player is currently driving.
			if ( iPlayerDamage )
			{
				if ( tr.m_pEnt->IsPlayer() )
				{
					flActualDamage = iPlayerDamage;
				}
#ifdef GAME_DLL
				else if ( tr.m_pEnt->GetServerVehicle() )
				{
					if ( tr.m_pEnt->GetServerVehicle()->GetPassenger() && tr.m_pEnt->GetServerVehicle()->GetPassenger()->IsPlayer() )
					{
						flActualDamage = iPlayerDamage;
					}
				}
#endif
			}

			int nActualDamageType = nDamageType;
			if ( flActualDamage == 0.0 )
			{
				flActualDamage = g_pGameRules->GetAmmoDamage( pAttacker, tr.m_pEnt, info.m_iAmmoType );
			}
			else
			{
				nActualDamageType = nDamageType | ((flActualDamage > 16) ? DMG_ALWAYSGIB : DMG_NEVERGIB );
			}

			if ( !bHitWater || ((info.m_nFlags & FIRE_BULLETS_DONT_HIT_UNDERWATER) == 0) )
			{
				// Damage specified by function parameter
				CTakeDamageInfo dmgInfo( this, pAttacker, flActualDamage, nActualDamageType );
				CalculateBulletDamageForce( &dmgInfo, info.m_iAmmoType, vecDir, tr.endpos );
				dmgInfo.ScaleDamageForce( info.m_flDamageForceScale );
				dmgInfo.SetAmmoType( info.m_iAmmoType );
				tr.m_pEnt->DispatchTraceAttack( dmgInfo, vecDir, &tr );
			
				if ( bStartedInWater || !bHitWater || (info.m_nFlags & FIRE_BULLETS_ALLOW_WATER_SURFACE_IMPACTS) )
				{
					if ( bDoServerEffects == true )
					{
						DoImpactEffect( tr, nDamageType );
					}
					else
					{
						bDoImpacts = true;
					}
				}
				else
				{
					// We may not impact, but we DO need to affect ragdolls on the client
					CEffectData data;
					data.m_vStart = tr.startpos;
					data.m_vOrigin = tr.endpos;
					data.m_nDamageType = nDamageType;
					
					DispatchEffect( "RagdollImpact", data );
				}
	
#ifdef GAME_DLL
				if ( nAmmoFlags & AMMO_FORCE_DROP_IF_CARRIED )
				{
					// Make sure if the player is holding this, he drops it
					Pickup_ForcePlayerToDropThisObject( tr.m_pEnt );		
				}
#endif
			}
		}
		
		// Now hit all triggers along the ray that respond to shots...
		// Clip the ray to the first collided solid returned from traceline
		CTakeDamageInfo triggerInfo( pAttacker, pAttacker, info.m_iDamage, nDamageType );
		CalculateBulletDamageForce( &triggerInfo, info.m_iAmmoType, vecDir, tr.endpos );
		triggerInfo.ScaleDamageForce( info.m_flDamageForceScale );
		triggerInfo.SetAmmoType( info.m_iAmmoType );
#ifdef GAME_DLL
		TraceAttackToTriggers( triggerInfo, tr.startpos, tr.endpos, vecDir );
#endif

		// See if we hit glass
		if ( tr.m_pEnt != NULL )
		{
#ifdef GAME_DLL
			surfacedata_t *psurf = physprops->GetSurfaceData( tr.surface.surfaceProps );
			if ( ( psurf != NULL ) && ( psurf->game.material == CHAR_TEX_GLASS ) && ( tr.m_pEnt->ClassMatches( "func_breakable" ) ) )
			{
				bHitGlass = true;
			}
#endif
		}

		if ( ( info.m_iTracerFreq != 0 ) && ( tracerCount++ % info.m_iTracerFreq ) == 0 && ( bHitGlass == false ) && !bRicochetBullet )
		{
			if ( bDoServerEffects == true )
			{
				Vector vecTracerSrc = vec3_origin;
				ComputeTracerStartPosition( info.m_vecSrc, &vecTracerSrc );

				trace_t Tracer;
				Tracer = tr;
				Tracer.endpos = vecTracerDest;
				MakeTracer( vecTracerSrc, Tracer, pAmmoDef->TracerType(info.m_iAmmoType) );
			}
			else
			{
				bDoTracers = true;
			}
		}

		//NOTENOTE: We could expand this to a more general solution for various material penetration types (wood, thin metal, etc)

		// See if we should pass through glass
#ifdef GAME_DLL
		if ( bHitGlass )
		{
			HandleShotImpactingGlass( info, tr, vecDir, &traceFilter );
		}
#endif

		if(!bRicochetBullet)
		{
			//The code to get the bullet to ricochet; this was ripped from the crossbow code.
			//calculate the vector, making sure to convert it to angles and back to vector
			//to get the lower possible form.  I dont know a better way to do this.
			Vector vecBulletDir = (tr.endpos - tr.startpos);
			QAngle angTemp;
			VectorAngles(vecBulletDir, angTemp);
			// See if we should reflect off this surface
			AngleVectors(angTemp, &vecBulletDir);
			float hitDot = DotProduct( tr.plane.normal, -vecBulletDir );

			//Now we get the hitDot modifier.

			surfacedata_t *pSurf = physprops->GetSurfaceData( tr.surface.surfaceProps );

			/*Msg("The physics dampening: %f\n", pSurf->physics.dampening);
			Msg("The physics density: %f\n", pSurf->physics.density);
			Msg("The physics friction: %f\n", pSurf->physics.friction);
			Msg("The physics elasticity: %f\n", pSurf->physics.elasticity);
			Msg("The physics thickness: %f\n", pSurf->physics.thickness);*/

			//this is very confusing, but just trust me; its a good way to decide if the
			//bullet should ricochet.
			if ( hitDot < (((pSurf->physics.density/3000)+pSurf->physics.elasticity)/2) )
			{
				//this decides the angle of the bullet after ricochet.
				//the RandomVector gives you a 5 degree angle change.
				vecTempDir = 2.0f * tr.plane.normal * hitDot + vecBulletDir + RandomVector(-0.04362, 0.04362);
				//make sure we are shooting another bullet from the ricochet point!
				iShot--;
				bRicochetBullet = true;
				//Msg("Bullet will ricochet.\n");
			}

		}
		else
		{
			bRicochetBullet = false;
		}

		iSeed++;
	}

#if defined( HL2MP ) && defined( GAME_DLL )
	if ( bDoServerEffects == false )
	{
		TE_HL2MPFireBullets( entindex(), tr.startpos, info.m_vecDirShooting, info.m_iAmmoType, iEffectSeed, info.m_iShots, info.m_vecSpread.x, bDoTracers, bDoImpacts );
	}
#endif

#ifdef GAME_DLL
	ApplyMultiDamage();
#endif
}

Ya, still a few debugging comments left over.
 
Although I have no coding experience what so ever, I'm impressed.

I hope it works as well as I hope it does :p
 
Dead-Inside said:
Although I have no coding experience what so ever, I'm impressed.

I hope it works as well as I hope it does :p

Hehe, most of that was already there; I just modified it. I only added about 10 lines, and modified 3 others. However, I am going to do a complete overhaul of it when I add penetration; right now, its sorta jury-rigged to work in a way it wasn't designed to.
 
snap man, good looking taking a shot at manipulating code yourself


an open source forum seems like a good idea.. hell a mod I'm helping is using some of the code from BG2 to learn off of (not sure if we actually used their code at the end)... so we should be fine to input the lessons we've learned from the HL2 SDK
 
Well I also got bullets to penetrate through walls and such, and depending on the walls density it can penetrate farther. Only problem is I cant get it to work with entities, such as physics objects. Like a table.
 
Back
Top