Thread: Hearing sounds
View Single Post
Re: Hearing sounds
Old
  (#3)
Pierre-Marie Baty
Roi de France
 
Pierre-Marie Baty's Avatar
 
Status: Offline
Posts: 5,049
Join Date: Nov 2003
Location: 46°43'60N 0°43'0W 0.187A
Default Re: Hearing sounds - 05-01-2004

I think I can boast to have the most complicated sound code in my bot - ever

In fact, sounds are of 2 types:
- "important" sounds are played by the server, which orders then some or all of its client to play it at the same time, through a network message.
- most of the other sounds are emulated by the client DLL, which interprets the game and decides if a sound must be played or not. This is done to save a whole lot of network bandwidth.

Only a few sounds are hookable through server-side functions, actually. All the rest is client-side stuff only.

Since bots have no client DLL, they need to emulate all the other sounds themselves. Movement sounds AND weapon sounds. Movement sounds can be evaluated each frame, while weapon sounds can be hooked by catching "ammo decrease" network messages.

Here's how I do it (here for emulating a Counter-Strike client DLL).
Code:
void PlayClientSoundsForBots (edict_t *pPlayer)
{
   // this function determines if the player pPlayer is walking or running, or climbing a ladder,
   // or landing on the ground, and so if he's likely to emit some client sound or not. Since
   // these types of sounds are predicted on the client side only, and bots have no client DLL,
   // we have to simulate their emitting in order for the bots to hear them. So in case a player
   // is moving, we bring his footstep sounds to the ears of the bots around. This sound is based
   // on the texture the player is walking on. Using TraceTexture(), we ask the engine for that
   // texture, then look up in the step sounds database in order to determine which footstep
   // sound is related to that texture. The ladder check then assumes that a player moving
   // vertically, not on the ground, having a ladder in his immediate surroundings is climbing
   // it, and the ladder sound is emitted periodically the same way footstep sounds are emitted.
   // Then, the landing check looks for non-null value of the player's punch angles (screen
   // tilting) while this player's damage inflictor be either null, or the world. If the test
   // success, a landing sound is emitted as well.
   // thanks to Tom Simpson from FoxBot for the water sounds handling
   edict_t *pGroundEntity = NULL;
   const char *texture_name, *player_weapon;
   char texture_type;
   char sound_path[256];
   int player_index;
   float player_velocity, volume;
   player_index = ENTINDEX (pPlayer) - 1; // get the player index
   if (DebugLevel.is_observer && !players[player_index].is_racc_bot)
	  return; // skip real players if in observer mode
   player_velocity = pPlayer->v.velocity.Length (); // get the player velocity
   player_weapon = STRING (pPlayer->v.weaponmodel) + 9; // get player's weapon, skip 'models/p_'
   // does the server allow footstep sounds AND this player is actually moving
   // AND is player on the ground AND is it time for him to make a footstep sound
   // OR has that player just landed on the ground after a jump ?
   if ((server.does_footsteps && IsOnFloor (pPlayer) && (player_velocity > 0)
		&& (players[player_index].step_sound_time < *server.time))
	   || ((FNullEnt (pPlayer->v.dmg_inflictor) || (pPlayer->v.dmg_inflictor == pWorldEntity))
		   && (pPlayer->v.punchangle != g_vecZero)
		   && !(players[player_index].prev_v.flags & (FL_ONGROUND | FL_PARTIALGROUND))))
   {
	  // is this player sloshing in water ?
	  if (pPlayer->v.waterlevel > 0)
	  {
		 sprintf (sound_path, "player/pl_slosh%d.wav", RANDOM_LONG (1, 4)); // build a slosh sound path
		 // bring slosh sound from this player to the bots' ears
		 DispatchSound (sound_path, pPlayer->v.origin + Vector (0, 0, -18), 0.9, ATTN_NORM);
		 players[player_index].step_sound_time = *server.time + 0.300; // next slosh in 300 milliseconds
	  }
	  // else this player is definitely not in water, does he move fast enough to make sounds ?
	  else if (player_velocity > MAX_WALK_SPEED)
	  {
		 // get the entity under the player's feet
		 if (!FNullEnt (pPlayer->v.groundentity))
			pGroundEntity = pPlayer->v.groundentity; // this player is standing over something
		 else
			pGroundEntity = pWorldEntity; // this player is standing over the world itself
		 // ask the engine for the texture name on pGroundEntity under the player's feet
		 texture_name = TRACE_TEXTURE (pGroundEntity, pPlayer->v.origin, Vector (0, 0, -9999));
		 // if the engine found the texture, ask the game DLL for the texture type
		 if (texture_name != NULL)
			texture_type = PM_FindTextureType ((char *) texture_name); // ask for texture type
		 // given the type of texture under player's feet, prepare a sound file for being played
		 switch (texture_type)
		 {
			default:
			case CHAR_TEX_CONCRETE:
			   sprintf (sound_path, "player/pl_step%d.wav", RANDOM_LONG (1, 4)); // 4 step sounds
			   volume = 0.9;
			   break;
			case CHAR_TEX_METAL:
			   sprintf (sound_path, "player/pl_metal%d.wav", RANDOM_LONG (1, 4)); // 4 metal sounds
			   volume = 0.9;
			   break;
			case CHAR_TEX_DIRT:
			   sprintf (sound_path, "player/pl_dirt%d.wav", RANDOM_LONG (1, 4)); // 4 dirt sounds
			   volume = 0.9;
			   break;
			case CHAR_TEX_VENT:
			   sprintf (sound_path, "player/pl_duct%d.wav", RANDOM_LONG (1, 4)); // 4 duct sounds
			   volume = 0.5;
			   break;
			case CHAR_TEX_GRATE:
			   sprintf (sound_path, "player/pl_grate%d.wav", RANDOM_LONG (1, 4)); // 4 grate sounds
			   volume = 0.9;
			   break;
			case CHAR_TEX_TILE:
			   sprintf (sound_path, "player/pl_tile%d.wav", RANDOM_LONG (1, 5)); // 5 tile sounds
			   volume = 0.8;
			   break;
			case CHAR_TEX_SLOSH:
			   sprintf (sound_path, "player/pl_slosh%d.wav", RANDOM_LONG (1, 4)); // 4 slosh sounds
			   volume = 0.9;
			   break;
			case CHAR_TEX_WOOD:
			   sprintf (sound_path, "debris/wood%d.wav", RANDOM_LONG (1, 3)); // 3 wood sounds
			   volume = 0.9;
			   break;
			case CHAR_TEX_GLASS:
			case CHAR_TEX_COMPUTER:
			   sprintf (sound_path, "debris/glass%d.wav", RANDOM_LONG (1, 4)); // 4 glass sounds
			   volume = 0.8;
			   break;
			case 'N':
			   sprintf (sound_path, "player/pl_snow%d.wav", RANDOM_LONG (1, 6)); // 6 snow sounds
			   volume = 0.8;
			   break;
		 }
		 // did we hit a breakable ?
		 if (!FNullEnt (pPlayer->v.groundentity)
			 && (strcmp ("func_breakable", STRING (pPlayer->v.groundentity->v.classname)) == 0))
			volume /= 1.5; // drop volume, the object will already play a damaged sound
		 // bring footstep sound from this player's feet to the bots' ears
		 DispatchSound (sound_path, pPlayer->v.origin + Vector (0, 0, -18), volume, ATTN_NORM);
		 players[player_index].step_sound_time = *server.time + 0.3; // next step in 300 milliseconds
	  }
   }
   // is this player completely in water AND it's time to play a wade sound
   // AND this player is pressing the jump key for swimming up ?
   if ((players[player_index].step_sound_time < *server.time)
	   && (pPlayer->v.waterlevel == 2) && (pPlayer->v.button & IN_JUMP))
   {
	  sprintf (sound_path, "player/pl_wade%d.wav", RANDOM_LONG (1, 4)); // build a wade sound path
	  // bring wade sound from this player to the bots' ears
	  DispatchSound (sound_path, pPlayer->v.origin + Vector (0, 0, -18), 0.9, ATTN_NORM);
	  players[player_index].step_sound_time = *server.time + 0.5; // next wade in 500 milliseconds
   }
   // now let's see if this player is on a ladder, for that we consider that he's not on the
   // ground, he's actually got a velocity (especially vertical), and that he's got a
   // func_ladder entity right in front of him. Is that player moving anormally NOT on ground ?
   if ((pPlayer->v.velocity.z != 0) && IsFlying (pPlayer) && !IsOnFloor (pPlayer))
   {
	  pGroundEntity = NULL; // first ensure the pointer at which to start the search is NULL
	  // cycle through all ladders...
	  while ((pGroundEntity = UTIL_FindEntityByString (pGroundEntity, "classname", "func_ladder")) != NULL)
	  {
		 // is this ladder at the same height as the player AND the player is next to it (in
		 // which case, assume he's climbing it), AND it's time for him to emit ladder sound ?
		 if ((pGroundEntity->v.absmin.z < pPlayer->v.origin.z) && (pGroundEntity->v.absmax.z > pPlayer->v.origin.z)
			 && (((pGroundEntity->v.absmin + pGroundEntity->v.absmax) / 2 - pPlayer->v.origin).Length2D () < 40)
			 && (players[player_index].step_sound_time < *server.time))
		 {
			volume = 0.8; // default volume for ladder sounds (empirical)
			// now build a random sound path amongst the 4 different ladder sounds
			sprintf (sound_path, "player/pl_ladder%d.wav", RANDOM_LONG (1, 4));
			// is the player ducking ?
			if (pPlayer->v.button & IN_DUCK)
			   volume /= 1.5; // drop volume, the player is trying to climb silently
			// bring ladder sound from this player's feet to the bots' ears
			DispatchSound (sound_path, pPlayer->v.origin + Vector (0, 0, -18), volume, ATTN_NORM);
			players[player_index].step_sound_time = *server.time + 0.500; // next in 500 milliseconds
		 }
	  }
   }
   // and now let's see if this player is pulling the pin of a grenade...
   if ((pPlayer->v.button & IN_ATTACK) && !(pPlayer->v.oldbuttons & IN_ATTACK)
	   && ((strcmp (player_weapon, "flashbang.mdl") == 0)
		   || (strcmp (player_weapon, "hegrenade.mdl") == 0)
		   || (strcmp (player_weapon, "smokegrenade.mdl") == 0)))
	  DispatchSound ("weapons/pinpull.wav", GetGunPosition (pPlayer), 1.0, ATTN_NORM);
   return;
}
Code:
void PlayBulletSoundsForBots (edict_t *pPlayer)
{
   // this function is in charge of emulating the gunshot sounds for the bots. Since these sounds
   // are only predicted by the client, and bots have no client DLL, obviously we have to do the
   // work for them. We consider a client is told gunshot sound occurs when he receives the
   // msg_CurWeapon network message, which gets sent whenever a player is lowering his ammo.
   // That's why we hook those messages in MessageBegin(), and send the entity responsible of
   // it have a walk around here. Given the weapon this player is holding in his hand then, the
   // appropriate gunshot sound is played, amongst all the sounds that are listed in the
   // weaponsounds.cfg file. Then DispatchSound() is called to bring the selected sound to the
   // bot's ears.
   weapon_t *pPlayerWeapon;
   const char *texture_name;
   char texture_type;
   int player_index, sound_index;
   Vector v_gun_position;
   player_index = ENTINDEX (pPlayer) - 1; // get the player index
   if (!IsValidPlayer (pPlayer) || !players[player_index].is_alive)
	  return; // skip invalid and dead players
   if (DebugLevel.is_observer && !players[player_index].is_racc_bot)
	  return; // skip real players if in observer mode
   if (!(pPlayer->v.button & (IN_ATTACK | IN_ATTACK2)))
	  return; // cancel if player is not firing
   if (STRING (pPlayer->v.weaponmodel)[0] == 0)
	  return; // cancel if player has no weapon
   pPlayerWeapon = FindWeaponByModel (STRING (pPlayer->v.weaponmodel)); // get player's weapon
   if (pPlayerWeapon->id == 0)
	  return; // cancel if player has no weapon
   v_gun_position = GetGunPosition (pPlayer); // get this player's gun position
   // now select the sound according to rail (primary or secondary) and mode
   if (pPlayer->v.button & IN_ATTACK)
   {
	  // primary rail
	  if (pPlayer->v.weaponanim == 0)
		 DispatchSound (pPlayerWeapon->primary.sound1, v_gun_position, 1.0, ATTN_NORM);
	  else
		 DispatchSound (pPlayerWeapon->primary.sound2, v_gun_position, 1.0, ATTN_NORM);
   }
   else
   {
	  // secondary rail
	  if (pPlayer->v.weaponanim == 0)
		 DispatchSound (pPlayerWeapon->secondary.sound1, v_gun_position, 1.0, ATTN_NORM);
	  else
		 DispatchSound (pPlayerWeapon->secondary.sound2, v_gun_position, 1.0, ATTN_NORM);
   }
   // do we have to worry about ricochet sounds ?
   if (ricochetsound_count > 0)
   {
	  // did this player's last traceline hit something AND it is not a player ?
	  if ((players[player_index].tr.flFraction < 1.0) && !FNullEnt (players[player_index].tr.pHit)
		  && !(players[player_index].tr.pHit->v.flags & (FL_MONSTER | FL_CLIENT)))
	  {
		 // ask the engine for the texture name at the bullet hit point
		 texture_name = TRACE_TEXTURE (players[player_index].tr.pHit, v_gun_position, players[player_index].tr.vecEndPos);
		 // if the engine found the texture, ask the MOD DLL for the texture type
		 if (texture_name != NULL)
			texture_type = PM_FindTextureType ((char *) texture_name); // ask for texture type
		 // loop through all the ricochet sounds the bot knows until we find the right one
		 for (sound_index = 0; sound_index < ricochetsound_count; sound_index++)
		 {
			// is it this texture type the bullet just hit OR have we reached the default sound ?
			if ((texture_type == ricochetsounds[sound_index].texture_type)
				|| (ricochetsounds[sound_index].texture_type == '*'))
			   break; // then no need to search further
		 }
		 // bring this ricochet sound to the bots' ears
		 DispatchSound (ricochetsounds[sound_index].file_path, players[player_index].tr.vecEndPos, 0.9, ATTN_NORM);
	  }
   }
   return;
}



RACC home - Bots-United: beer, babies & bots (especially the latter)
"Learn to think by yourself, else others will do it for you."
  
Reply With Quote