text: /***
  *
  *	Copyright (c) 1996-2002, Valve LLC. All rights reserved.
  *	
  *	This product contains software technology licensed from Id 
  *	Software, Inc. ("Id Technology").  Id Technology (c) 1996 Id Software, Inc. 
  *	All Rights Reserved.
  *
  *   Use, distribution, and modification of this source code and/or resulting
  *   object code is restricted to non-commercial enhancements to products from
  *   Valve LLC.  All other use, distribution, or modification is prohibited
  *   without written permission from Valve LLC.
  *
  ****/
  /*
  
  ===== combat.cpp ========================================================
  
    functions dealing with damage infliction & death
  
  */
  
  #include "extdll.h"
  #include "util.h"
  #include "cbase.h"
  #include "monsters.h"
  #include "soundent.h"
  #include "decals.h"
  #include "animation.h"
  #include "weapons.h"
  #include "func_break.h"
  #include "player.h"
  
  extern DLL_GLOBAL Vector		g_vecAttackDir;
  extern DLL_GLOBAL int			g_iSkillLevel;
  
  extern Vector VecBModelOrigin( entvars_t* pevBModel );
  extern entvars_t *g_pevLastInflictor;
  
  unsigned short g_sGibbed;
  unsigned short g_sTeleport;
  unsigned short g_sTrail;
  unsigned short g_sExplosion;
  unsigned short g_usPowerUp;
  
  #define GERMAN_GIB_COUNT		4
  #define	HUMAN_GIB_COUNT			6
  #define ALIEN_GIB_COUNT			4
  
  
  // HACKHACK -- The gib velocity equations don't work
  void CGib :: LimitVelocity( void )
  {
  	float length = pev->velocity.Length();
  
  	// ceiling at 1500.  The gib velocity equation is not bounded properly.  Rather than tune it
  	// in 3 separate places again, I'll just limit it here.
  	if ( length > 1500.0 )
  		pev->velocity = pev->velocity.Normalize() * 1500;		// This should really be sv_maxvelocity * 0.75 or something
  }
  
  
  void CGib :: SpawnStickyGibs( entvars_t *pevVictim, Vector vecOrigin, int cGibs )
  {
  	int i;
  
  	if ( g_Language == LANGUAGE_GERMAN )
  	{
  		// no sticky gibs in germany right now!
  		return; 
  	}
  
  	for ( i = 0 ; i < cGibs ; i++ )
  	{
  		CGib *pGib = GetClassPtr( (CGib *)NULL );
  
  		pGib->Spawn( "models/stickygib.mdl" );
  		pGib->pev->body = RANDOM_LONG(0,2);
  
  		if ( pevVictim )
  		{
  			pGib->pev->origin.x = vecOrigin.x + RANDOM_FLOAT( -3, 3 );
  			pGib->pev->origin.y = vecOrigin.y + RANDOM_FLOAT( -3, 3 );
  			pGib->pev->origin.z = vecOrigin.z + RANDOM_FLOAT( -3, 3 );
  
  			/*
  			pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) );
  			pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) );
  			pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) );
  			*/
  
  			// make the gib fly away from the attack vector
  			pGib->pev->velocity = g_vecAttackDir * -1;
  
  			// mix in some noise
  			pGib->pev->velocity.x += RANDOM_FLOAT ( -0.15, 0.15 );
  			pGib->pev->velocity.y += RANDOM_FLOAT ( -0.15, 0.15 );
  			pGib->pev->velocity.z += RANDOM_FLOAT ( -0.15, 0.15 );
  
  			pGib->pev->velocity = pGib->pev->velocity * 900;
  
  			pGib->pev->avelocity.x = RANDOM_FLOAT ( 250, 400 );
  			pGib->pev->avelocity.y = RANDOM_FLOAT ( 250, 400 );
  
  			// copy owner's blood color
  			pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor();
  		
  			if ( pevVictim->health > -50)
  			{
  				pGib->pev->velocity = pGib->pev->velocity * 0.7;
  			}
  			else if ( pevVictim->health > -200)
  			{
  				pGib->pev->velocity = pGib->pev->velocity * 2;
  			}
  			else
  			{
  				pGib->pev->velocity = pGib->pev->velocity * 4;
  			}
  
  			
  			pGib->pev->movetype = MOVETYPE_TOSS;
  			pGib->pev->solid = SOLID_BBOX;
  			UTIL_SetSize ( pGib->pev, Vector ( 0, 0 ,0 ), Vector ( 0, 0, 0 ) );
  			pGib->SetTouch ( &CGib::StickyGibTouch );
  			pGib->SetThink (NULL);
  		}
  		pGib->LimitVelocity();
  	}
  }
  
  void CGib :: SpawnHeadGib( entvars_t *pevVictim )
  {
  	CGib *pGib = GetClassPtr( (CGib *)NULL );
  
  	if ( g_Language == LANGUAGE_GERMAN )
  	{
  		pGib->Spawn( "models/germangibs.mdl" );// throw one head
  		pGib->pev->body = 0;
  	}
  	else
  	{
  		pGib->Spawn( "models/hgibs.mdl" );// throw one head
  		pGib->pev->body = 0;
  	}
  
  	if ( pevVictim )
  	{
  		pGib->pev->origin = pevVictim->origin + pevVictim->view_ofs;
  		
  		edict_t		*pentPlayer = FIND_CLIENT_IN_PVS( pGib->edict() );
  		
  		if ( RANDOM_LONG ( 0, 100 ) <= 5 && pentPlayer )
  		{
  			// 5% chance head will be thrown at player's face.
  			entvars_t	*pevPlayer;
  
  			pevPlayer = VARS( pentPlayer );
  			pGib->pev->velocity = ( ( pevPlayer->origin + pevPlayer->view_ofs ) - pGib->pev->origin ).Normalize() * 300;
  			pGib->pev->velocity.z += 100;
  		}
  		else
  		{
  			pGib->pev->velocity = Vector (RANDOM_FLOAT(-100,100), RANDOM_FLOAT(-100,100), RANDOM_FLOAT(200,300));
  		}
  
  
  		pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 );
  		pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 );
  
  		// copy owner's blood color
  		pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor();
  	
  		if ( pevVictim->health > -50)
  		{
  			pGib->pev->velocity = pGib->pev->velocity * 0.7;
  		}
  		else if ( pevVictim->health > -200)
  		{
  			pGib->pev->velocity = pGib->pev->velocity * 2;
  		}
  		else
  		{
  			pGib->pev->velocity = pGib->pev->velocity * 4;
  		}
  	}
  	pGib->LimitVelocity();
  }
  
  void CGib :: SpawnRandomGibs( entvars_t *pevVictim, int cGibs, int human )
  {
  	int cSplat;
  
  	for ( cSplat = 0 ; cSplat < cGibs ; cSplat++ )
  	{
  		CGib *pGib = GetClassPtr( (CGib *)NULL );
  
  		if ( g_Language == LANGUAGE_GERMAN )
  		{
  			pGib->Spawn( "models/germangibs.mdl" );
  			pGib->pev->body = RANDOM_LONG(0,GERMAN_GIB_COUNT-1);
  		}
  		else
  		{
  			if ( human )
  			{
  				// human pieces
  				pGib->Spawn( "models/hgibs.mdl" );
  				pGib->pev->body = RANDOM_LONG(1,HUMAN_GIB_COUNT-1);// start at one to avoid throwing random amounts of skulls (0th gib)
  			}
  			else
  			{
  				// aliens
  				pGib->Spawn( "models/agibs.mdl" );
  				pGib->pev->body = RANDOM_LONG(0,ALIEN_GIB_COUNT-1);
  			}
  		}
  
  		if ( pevVictim )
  		{
  			// spawn the gib somewhere in the monster's bounding volume
  			pGib->pev->origin.x = pevVictim->absmin.x + pevVictim->size.x * (RANDOM_FLOAT ( 0 , 1 ) );
  			pGib->pev->origin.y = pevVictim->absmin.y + pevVictim->size.y * (RANDOM_FLOAT ( 0 , 1 ) );
  			pGib->pev->origin.z = pevVictim->absmin.z + pevVictim->size.z * (RANDOM_FLOAT ( 0 , 1 ) ) + 1;	// absmin.z is in the floor because the engine subtracts 1 to enlarge the box
  
  			// make the gib fly away from the attack vector
  			pGib->pev->velocity = g_vecAttackDir * -1;
  
  			// mix in some noise
  			pGib->pev->velocity.x += RANDOM_FLOAT ( -0.25, 0.25 );
  			pGib->pev->velocity.y += RANDOM_FLOAT ( -0.25, 0.25 );
  			pGib->pev->velocity.z += RANDOM_FLOAT ( -0.25, 0.25 );
  
  			pGib->pev->velocity = pGib->pev->velocity * RANDOM_FLOAT ( 300, 400 );
  
  			pGib->pev->avelocity.x = RANDOM_FLOAT ( 100, 200 );
  			pGib->pev->avelocity.y = RANDOM_FLOAT ( 100, 300 );
  
  			// copy owner's blood color
  			pGib->m_bloodColor = (CBaseEntity::Instance(pevVictim))->BloodColor();
  			
  			if ( pevVictim->health > -50)
  			{
  				pGib->pev->velocity = pGib->pev->velocity * 0.7;
  			}
  			else if ( pevVictim->health > -200)
  			{
  				pGib->pev->velocity = pGib->pev->velocity * 2;
  			}
  			else
  			{
  				pGib->pev->velocity = pGib->pev->velocity * 4;
  			}
  
  			pGib->pev->solid = SOLID_BBOX;
  			UTIL_SetSize ( pGib->pev, Vector( 0 , 0 , 0 ), Vector ( 0, 0, 0 ) );
  		}
  		pGib->LimitVelocity();
  	}
  }
  
  //=========================================================
  // WaitTillLand - in order to emit their meaty scent from
  // the proper location, gibs should wait until they stop 
  // bouncing to emit their scent. That's what this function
  // does.
  //=========================================================
  void CGib :: WaitTillLand ( void )
  {
  	if (!IsInWorld())
  	{
  		UTIL_Remove( this );
  		return;
  	}
  
  	if ( pev->velocity == g_vecZero )
  	{
  		SetThink (&CGib::SUB_StartFadeOut);
  		pev->nextthink = gpGlobals->time + m_lifeTime;
  	}
  	else
  	{
  		// wait and check again in another half second.
  		pev->nextthink = gpGlobals->time + 0.5;
  	}
  }
  
  //
  // Gib bounces on the ground or wall, sponges some blood down, too!
  //
  void CGib :: BounceGibTouch ( CBaseEntity *pOther )
  {
  	Vector	vecSpot;
  	TraceResult	tr;
  	
  	//if ( RANDOM_LONG(0,1) )
  	//	return;// don't bleed everytime
  
  	if (pev->flags & FL_ONGROUND)
  	{
  		pev->velocity = pev->velocity * 0.9;
  		pev->angles.x = 0;
  		pev->angles.z = 0;
  		pev->avelocity.x = 0;
  		pev->avelocity.z = 0;
  	}
  	else
  	{
  		if ( g_Language != LANGUAGE_GERMAN && m_cBloodDecals > 0 && m_bloodColor != DONT_BLEED )
  		{
  			vecSpot = pev->origin + Vector ( 0 , 0 , 8 );//move up a bit, and trace down.
  			UTIL_TraceLine ( vecSpot, vecSpot + Vector ( 0, 0, -24 ),  ignore_monsters, ENT(pev), & tr);
  
  			UTIL_BloodDecalTrace( &tr, m_bloodColor );
  
  			m_cBloodDecals--; 
  		}
  
  		if ( m_material != matNone && RANDOM_LONG(0,2) == 0 )
  		{
  			float volume;
  			float zvel = fabs(pev->velocity.z);
  		
  			volume = 0.8 * V_min(1.0, ((float)zvel) / 450.0);
  
  			CBreakable::MaterialSoundRandom( edict(), (Materials)m_material, volume );
  		}
  	}
  }
  
  //
  // Sticky gib puts blood on the wall and stays put. 
  //
  void CGib :: StickyGibTouch ( CBaseEntity *pOther )
  {
  	Vector	vecSpot;
  	TraceResult	tr;
  	
  	SetThink ( &CGib::SUB_Remove );
  	pev->nextthink = gpGlobals->time + 10;
  
  	if ( !FClassnameIs( pOther->pev, "worldspawn" ) )
  	{
  		pev->nextthink = gpGlobals->time;
  		return;
  	}
  
  	UTIL_TraceLine ( pev->origin, pev->origin + pev->velocity * 32,  ignore_monsters, ENT(pev), & tr);
  
  	UTIL_BloodDecalTrace( &tr, m_bloodColor );
  
  	pev->velocity = tr.vecPlaneNormal * -1;
  	pev->angles = UTIL_VecToAngles ( pev->velocity );
  	pev->velocity = g_vecZero; 
  	pev->avelocity = g_vecZero;
  	pev->movetype = MOVETYPE_NONE;
  }
  
  //=========================================================
  // GibMonster - create some gore and get rid of a monster's
  // model.
  //=========================================================
  void CBaseMonster :: GibMonster( void )
  {
  	TraceResult	tr;
  	BOOL		gibbed = FALSE;
  
  	EMIT_SOUND(ENT(pev), CHAN_WEAPON, "common/bodysplat.wav", 1, ATTN_NORM);		
  
  	if ( CVAR_GET_FLOAT("violence_hgibs") != 0 )	// Only the player will ever get here
  	{
  		if ( IsPlayer() )
  		{
  			PLAYBACK_EVENT_FULL ( FEV_GLOBAL, edict(), 
  			g_sGibbed, 0.0, (float *)&pev->origin, (float *)&g_vecAttackDir, 0.0, 0.0, 0, 0, 0, 0);
  		}
  
  		/*CGib::SpawnHeadGib( pev );
  		CGib::SpawnRandomGibs( pev, 4, 1 );	// throw some human gibs.*/
  	}
  	gibbed = TRUE;
  }
  
  //=========================================================
  // GetDeathActivity - determines the best type of death
  // anim to play.
  //=========================================================
  Activity CBaseMonster :: GetDeathActivity ( void )
  {
  	Activity	deathActivity;
  	BOOL		fTriedDirection;
  	float		flDot;
  	TraceResult	tr;
  	Vector		vecSrc;
  
  	if ( pev->deadflag != DEAD_NO )
  	{
  		// don't run this while dying.
  		return m_IdealActivity;
  	}
  
  	vecSrc = Center();
  
  	fTriedDirection = FALSE;
  	deathActivity = ACT_DIESIMPLE;// in case we can't find any special deaths to do.
  
  	UTIL_MakeVectors ( pev->angles );
  	flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 );
  
  	switch ( m_LastHitGroup )
  	{
  		// try to pick a region-specific death.
  	case HITGROUP_HEAD:
  		deathActivity = ACT_DIE_HEADSHOT;
  		break;
  
  	case HITGROUP_STOMACH:
  		deathActivity = ACT_DIE_GUTSHOT;
  		break;
  
  	case HITGROUP_GENERIC:
  		// try to pick a death based on attack direction
  		fTriedDirection = TRUE;
  
  		if ( flDot > 0.3 )
  		{
  			deathActivity = ACT_DIEFORWARD;
  		}
  		else if ( flDot <= -0.3 )
  		{
  			deathActivity = ACT_DIEBACKWARD;
  		}
  		break;
  
  	default:
  		// try to pick a death based on attack direction
  		fTriedDirection = TRUE;
  
  		if ( flDot > 0.3 )
  		{
  			deathActivity = ACT_DIEFORWARD;
  		}
  		else if ( flDot <= -0.3 )
  		{
  			deathActivity = ACT_DIEBACKWARD;
  		}
  		break;
  	}
  
  
  	// can we perform the prescribed death?
  	if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE )
  	{
  		// no! did we fail to perform a directional death? 
  		if ( fTriedDirection )
  		{
  			// if yes, we're out of options. Go simple.
  			deathActivity = ACT_DIESIMPLE;
  		}
  		else
  		{
  			// cannot perform the ideal region-specific death, so try a direction.
  			if ( flDot > 0.3 )
  			{
  				deathActivity = ACT_DIEFORWARD;
  			}
  			else if ( flDot <= -0.3 )
  			{
  				deathActivity = ACT_DIEBACKWARD;
  			}
  		}
  	}
  
  	if ( LookupActivity ( deathActivity ) == ACTIVITY_NOT_AVAILABLE )
  	{
  		// if we're still invalid, simple is our only option.
  		deathActivity = ACT_DIESIMPLE;
  	}
  
  	if ( deathActivity == ACT_DIEFORWARD )
  	{
  			// make sure there's room to fall forward
  			UTIL_TraceHull ( vecSrc, vecSrc + gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr );
  
  			if ( tr.flFraction != 1.0 )
  			{
  				deathActivity = ACT_DIESIMPLE;
  			}
  	}
  
  	if ( deathActivity == ACT_DIEBACKWARD )
  	{
  			// make sure there's room to fall backward
  			UTIL_TraceHull ( vecSrc, vecSrc - gpGlobals->v_forward * 64, dont_ignore_monsters, head_hull, edict(), &tr );
  
  			if ( tr.flFraction != 1.0 )
  			{
  				deathActivity = ACT_DIESIMPLE;
  			}
  	}
  
  	return deathActivity;
  }
  
  //=========================================================
  // GetSmallFlinchActivity - determines the best type of flinch
  // anim to play.
  //=========================================================
  Activity CBaseMonster :: GetSmallFlinchActivity ( void )
  {
  	Activity	flinchActivity;
  	BOOL		fTriedDirection;
  	float		flDot;
  
  	fTriedDirection = FALSE;
  	UTIL_MakeVectors ( pev->angles );
  	flDot = DotProduct ( gpGlobals->v_forward, g_vecAttackDir * -1 );
  	
  	switch ( m_LastHitGroup )
  	{
  		// pick a region-specific flinch
  	case HITGROUP_HEAD:
  		flinchActivity = ACT_FLINCH_HEAD;
  		break;
  	case HITGROUP_STOMACH:
  		flinchActivity = ACT_FLINCH_STOMACH;
  		break;
  	case HITGROUP_LEFTARM:
  		flinchActivity = ACT_FLINCH_LEFTARM;
  		break;
  	case HITGROUP_RIGHTARM:
  		flinchActivity = ACT_FLINCH_RIGHTARM;
  		break;
  	case HITGROUP_LEFTLEG:
  		flinchActivity = ACT_FLINCH_LEFTLEG;
  		break;
  	case HITGROUP_RIGHTLEG:
  		flinchActivity = ACT_FLINCH_RIGHTLEG;
  		break;
  	case HITGROUP_GENERIC:
  	default:
  		// just get a generic flinch.
  		flinchActivity = ACT_SMALL_FLINCH;
  		break;
  	}
  
  
  	// do we have a sequence for the ideal activity?
  	if ( LookupActivity ( flinchActivity ) == ACTIVITY_NOT_AVAILABLE )
  	{
  		flinchActivity = ACT_SMALL_FLINCH;
  	}
  
  	return flinchActivity;
  }
  
  
  BOOL CBaseMonster::ShouldGibMonster( int iGib )
  {
  	if ( ( iGib == GIB_NORMAL && pev->health < GIB_HEALTH_VALUE ) || ( iGib == GIB_ALWAYS ) )
  		return TRUE;
  	
  	return FALSE;
  }
  
  
  void CBaseMonster::CallGibMonster( void )
  {
  	BOOL fade = FALSE;
  
  	if ( HasHumanGibs() )
  	{
  		if ( CVAR_GET_FLOAT("violence_hgibs") == 0 )
  			fade = TRUE;
  	}
  	else if ( HasAlienGibs() )
  	{
  		if ( CVAR_GET_FLOAT("violence_agibs") == 0 )
  			fade = TRUE;
  	}
  
  	pev->takedamage = DAMAGE_NO;
  	pev->solid = SOLID_NOT;// do something with the body. while monster blows up
  
  	if ( fade )
  	{
  		FadeMonster();
  	}
  	else
  	{
  		pev->effects = EF_NODRAW; // make the model invisible.
  		GibMonster();
  	}
  
  	pev->deadflag = DEAD_DEAD;
  	FCheckAITrigger();
  
  	// don't let the status bar glitch for players.with <0 health.
  	if (pev->health < -99)
  	{
  		pev->health = 0;
  	}
  	
  	if ( ShouldFadeOnDeath() && !fade )
  		UTIL_Remove(this);
  }
  
  //
  // fade out - slowly fades a entity out, then removes it.
  //
  // DON'T USE ME FOR GIBS AND STUFF IN MULTIPLAYER! 
  // SET A FUTURE THINK AND A RENDERMODE!!
  void CBaseEntity :: SUB_StartFadeOut ( void )
  {
  	if (pev->rendermode == kRenderNormal)
  	{
  		pev->renderamt = 255;
  		pev->rendermode = kRenderTransTexture;
  	}
  
  	pev->solid = SOLID_NOT;
  	pev->avelocity = g_vecZero;
  
  	pev->nextthink = gpGlobals->time + 0.1;
  	SetThink ( &CBaseEntity::SUB_FadeOut );
  }
  
  void CBaseEntity :: SUB_FadeOut ( void  )
  {
  	if ( pev->renderamt > 7 )
  	{
  		pev->renderamt -= 7;
  		pev->nextthink = gpGlobals->time + 0.1;
  	}
  	else 
  	{
  		pev->renderamt = 0;
  		pev->nextthink = gpGlobals->time + 0.2;
  		SetThink ( &CBaseEntity::SUB_Remove );
  	}
  }
  
  //
  // Throw a chunk
  //
  void CGib :: Spawn( const char *szGibModel )
  {
  	pev->movetype = MOVETYPE_BOUNCE;
  	pev->friction = 0.55; // deading the bounce a bit
  	
  	// sometimes an entity inherits the edict from a former piece of glass,
  	// and will spawn using the same render FX or rendermode! bad!
  	pev->renderamt = 255;
  	pev->rendermode = kRenderNormal;
  	pev->renderfx = kRenderFxNone;
  	pev->solid = SOLID_SLIDEBOX;/// hopefully this will fix the VELOCITY TOO LOW crap
  	pev->classname = MAKE_STRING("gib");
  
  	SET_MODEL(ENT(pev), szGibModel);
  	UTIL_SetSize(pev, Vector( 0, 0, 0), Vector(0, 0, 0));
  
  	pev->nextthink = gpGlobals->time + 4;
  	m_lifeTime = 25;
  	SetThink ( &CGib::WaitTillLand );
  	SetTouch ( &CGib::BounceGibTouch );
  
  	m_material = matNone;
  	m_cBloodDecals = 5;// how many blood decals this gib can place (1 per bounce until none remain). 
  }
  
  /*
  ============
  TakeDamage
  
  The damage is coming from inflictor, but get mad at attacker
  This should be the only function that ever reduces health.
  bitsDamageType indicates the type of damage sustained, ie: DMG_SHOCK
  
  Time-based damage: only occurs while the monster is within the trigger_hurt.
  When a monster is poisoned via an arrow etc it takes all the poison damage at once.
  
  
  
  GLOBALS ASSUMED SET:  g_iSkillLevel
  ============
  */
  int CBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
  {
  	float	flTake;
  	Vector	vecDir;
  
  	if (!pev->takedamage)
  		return 0;
  
  	if ( !IsAlive() )
  	{
  		return DeadTakeDamage( pevInflictor, pevAttacker, flDamage, bitsDamageType );
  	}
  
  	if ( pev->deadflag == DEAD_NO )
  	{
  		// no pain sound during death animation.
  		PainSound();// "Ouch!"
  	}
  
  	//!!!LATER - make armor consideration here!
  	flTake = flDamage;
  
  	// set damage type sustained
  	m_bitsDamageType |= bitsDamageType;
  
  	// grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit).
  	vecDir = Vector( 0, 0, 0 );
  	if (!FNullEnt( pevInflictor ))
  	{
  		CBaseEntity *pInflictor = CBaseEntity :: Instance( pevInflictor );
  		if (pInflictor)
  		{
  			vecDir = ( pInflictor->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize();
  			vecDir = g_vecAttackDir = vecDir.Normalize();
  		}
  	}
  
  	// add to the damage total for clients, which will be sent as a single
  	// message at the end of the frame
  	// todo: remove after combining shotgun blasts?
  	if ( IsPlayer() )
  	{
  		if ( pevInflictor )
  			pev->dmg_inflictor = ENT(pevInflictor);
  
  		pev->dmg_take += flTake;
  
  		// check for godmode or invincibility
  		if ( pev->flags & FL_GODMODE )
  		{
  			return 0;
  		}
  	}
  
  	// if this is a player, move him around!
  	if ( ( !FNullEnt( pevInflictor ) ) && (pev->movetype == MOVETYPE_WALK) && (!pevAttacker || pevAttacker->solid != SOLID_TRIGGER) )
  	{
  		pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage );
  	}
  
  	// do the damage
  	pev->health -= flTake;
  
  	// HACKHACK Don't kill monsters in a script.  Let them break their scripts first
  	if ( m_MonsterState == MONSTERSTATE_SCRIPT )
  	{
  		SetConditions( bits_COND_LIGHT_DAMAGE );
  		return 0;
  	}
  
  	if ( pev->health <= 0 )
  	{
  		g_pevLastInflictor = pevInflictor;
  
  		if ( bitsDamageType & DMG_ALWAYSGIB )
  		{
  			Killed( pevAttacker, GIB_ALWAYS );
  		}
  		else if ( bitsDamageType & DMG_NEVERGIB )
  		{
  			Killed( pevAttacker, GIB_NEVER );
  		}
  		else
  		{
  			Killed( pevAttacker, GIB_NORMAL );
  		}
  
  		g_pevLastInflictor = NULL;
  
  		return 0;
  	}
  
  	// react to the damage (get mad)
  	if ( (pev->flags & FL_MONSTER) && !FNullEnt(pevAttacker) )
  	{
  		if ( pevAttacker->flags & (FL_MONSTER | FL_CLIENT) )
  		{// only if the attack was a monster or client!
  			
  			// enemy's last known position is somewhere down the vector that the attack came from.
  			if (pevInflictor)
  			{
  				if (m_hEnemy == NULL || pevInflictor == m_hEnemy->pev || !HasConditions(bits_COND_SEE_ENEMY))
  				{
  					m_vecEnemyLKP = pevInflictor->origin;
  				}
  			}
  			else
  			{
  				m_vecEnemyLKP = pev->origin + ( g_vecAttackDir * 64 ); 
  			}
  
  			MakeIdealYaw( m_vecEnemyLKP );
  
  			// add pain to the conditions 
  			// !!!HACKHACK - fudged for now. Do we want to have a virtual function to determine what is light and 
  			// heavy damage per monster class?
  			if ( flDamage > 0 )
  			{
  				SetConditions(bits_COND_LIGHT_DAMAGE);
  			}
  
  			if ( flDamage >= 20 )
  			{
  				SetConditions(bits_COND_HEAVY_DAMAGE);
  			}
  		}
  	}
  
  	return 1;
  }
  
  //=========================================================
  // DeadTakeDamage - takedamage function called when a monster's
  // corpse is damaged.
  //=========================================================
  int CBaseMonster :: DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
  {
  	Vector			vecDir;
  
  	// grab the vector of the incoming attack. ( pretend that the inflictor is a little lower than it really is, so the body will tend to fly upward a bit).
  	vecDir = Vector( 0, 0, 0 );
  	if (!FNullEnt( pevInflictor ))
  	{
  		CBaseEntity *pInflictor = CBaseEntity :: Instance( pevInflictor );
  		if (pInflictor)
  		{
  			vecDir = ( pInflictor->Center() - Vector ( 0, 0, 10 ) - Center() ).Normalize();
  			vecDir = g_vecAttackDir = vecDir.Normalize();
  		}
  	}
  
  #if 0// turn this back on when the bounding box issues are resolved.
  
  	pev->flags &= ~FL_ONGROUND;
  	pev->origin.z += 1;
  	
  	// let the damage scoot the corpse around a bit.
  	if ( !FNullEnt(pevInflictor) && (pevAttacker->solid != SOLID_TRIGGER) )
  	{
  		pev->velocity = pev->velocity + vecDir * -DamageForce( flDamage );
  	}
  
  #endif
  
  	// kill the corpse if enough damage was done to destroy the corpse and the damage is of a type that is allowed to destroy the corpse.
  	if ( bitsDamageType & DMG_GIB_CORPSE )
  	{
  		if ( pev->health <= flDamage )
  		{
  			pev->health = -50;
  			Killed( pevAttacker, GIB_ALWAYS );
  			return 0;
  		}
  		// Accumulate corpse gibbing damage, so you can gib with multiple hits
  		pev->health -= flDamage * 0.1;
  	}
  	
  	return 1;
  }
  
  
  float CBaseMonster :: DamageForce( float damage )
  { 
  	float force = damage * ((32 * 32 * 72.0) / (pev->size.x * pev->size.y * pev->size.z)) * 5;
  	
  	if ( force > 1000.0) 
  	{
  		force = 1000.0;
  	}
  
  	return force;
  }
  
  //
  // RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range.
  // 
  // only damage ents that can clearly be seen by the explosion!
  
  	
  void RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, float flRadius, int iClassIgnore, int bitsDamageType )
  {
  	CBaseEntity *pEntity = NULL;
  	TraceResult	tr;
  	float		flAdjustedDamage, falloff;
  	Vector		vecSpot;
  
  	if ( flRadius )
  		falloff = flDamage / flRadius;
  	else
  		falloff = 1.0;
  
  	int bInWater = (UTIL_PointContents ( vecSrc ) == CONTENTS_WATER);
  
  	vecSrc.z += 1;// in case grenade is lying on the ground
  
  	if ( !pevAttacker )
  		pevAttacker = pevInflictor;
  
  	// iterate on all entities in the vicinity.
  	while ((pEntity = UTIL_FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL)
  	{
  		if ( pEntity->pev->takedamage != DAMAGE_NO )
  		{
  			// UNDONE: this should check a damage mask, not an ignore
  			if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
  			{// houndeyes don't hurt other houndeyes with their attack
  				continue;
  			}
  
  			// blast's don't tavel into or out of water
  			if (bInWater && pEntity->pev->waterlevel == 0)
  				continue;
  			if (!bInWater && pEntity->pev->waterlevel == 3)
  				continue;
  
  			vecSpot = pEntity->BodyTarget( vecSrc );
  			
  			UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, ENT(pevInflictor), &tr );
  
  			if ( tr.flFraction == 1.0 || tr.pHit == pEntity->edict() )
  			{// the explosion can 'see' this entity, so hurt them!
  				if (tr.fStartSolid)
  				{
  					// if we're stuck inside them, fixup the position and distance
  					tr.vecEndPos = vecSrc;
  					tr.flFraction = 0.0;
  				}
  				
  				// decrease damage for an ent that's farther from the bomb.
  				flAdjustedDamage = ( vecSrc - tr.vecEndPos ).Length() * falloff;
  				flAdjustedDamage = flDamage - flAdjustedDamage;
  			
  				if ( flAdjustedDamage < 0 )
  				{
  					flAdjustedDamage = 0;
  				}
  			
  				// ALERT( at_console, "hit %s\n", STRING( pEntity->pev->classname ) );
  				if (tr.flFraction != 1.0)
  				{
  					ClearMultiDamage( );
  					pEntity->TraceAttack( pevInflictor, flAdjustedDamage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, bitsDamageType );
  					ApplyMultiDamage( pevInflictor, pevAttacker );
  				}
  				else
  				{
  					pEntity->TakeDamage ( pevInflictor, pevAttacker, flAdjustedDamage, bitsDamageType );
  				}
  			}
  		}
  	}
  }
  
  
  void CBaseMonster :: RadiusDamage(entvars_t* pevInflictor, entvars_t*	pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType )
  {
  	::RadiusDamage( pev->origin, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType );
  }
  
  
  void CBaseMonster :: RadiusDamage( Vector vecSrc, entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int iClassIgnore, int bitsDamageType )
  {
  	::RadiusDamage( vecSrc, pevInflictor, pevAttacker, flDamage, flDamage * 2.5, iClassIgnore, bitsDamageType );
  }
  
  /*
  //=========================================================
  // TraceAttack
  //=========================================================
  void CBaseMonster::TraceAttack(entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType)
  {
  	Vector vecOrigin = ptr->vecEndPos - vecDir * 4;
  
  	ALERT ( at_console, "%d\n", ptr->iHitgroup );
  
  
  	if ( pev->takedamage )
  	{
  		AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
  
  		int blood = BloodColor();
  		
  		if ( blood != DONT_BLEED )
  		{
  			SpawnBlood(vecOrigin, blood, flDamage);// a little surface blood.
  		}
  	}
  }
  */
  
  void CBaseEntity :: TraceBleed( float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
  {
  	if (BloodColor() == DONT_BLEED)
  		return;
  	
  	if (flDamage == 0)
  		return;
  
  	if (! (bitsDamageType & (DMG_CRUSH | DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB | DMG_MORTAR)))
  		return;
  	
  	// make blood decal on the wall! 
  	TraceResult Bloodtr;
  	Vector vecTraceDir; 
  	float flNoise;
  	int cCount;
  	int i;
  
  	if (flDamage < 10)
  	{
  		flNoise = 0.1;
  		cCount = 1;
  	}
  	else if (flDamage < 25)
  	{
  		flNoise = 0.2;
  		cCount = 2;
  	}
  	else
  	{
  		flNoise = 0.3;
  		cCount = 4;
  	}
  
  	for ( i = 0 ; i < cCount ; i++ )
  	{
  		vecTraceDir = vecDir * -1;// trace in the opposite direction the shot came from (the direction the shot is going)
  
  		vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise );
  		vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise );
  		vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise );
  
  		UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * -172, ignore_monsters, ENT(pev), &Bloodtr);
  
  		if ( Bloodtr.flFraction != 1.0 )
  		{
  			UTIL_BloodDecalTrace( &Bloodtr, BloodColor() );
  		}
  	}
  }
  
  //=========================================================
  //=========================================================
  void CBaseMonster :: MakeDamageBloodDecal ( int cCount, float flNoise, TraceResult *ptr, const Vector &vecDir )
  {
  	// make blood decal on the wall! 
  	TraceResult Bloodtr;
  	Vector vecTraceDir; 
  	int i;
  
  	if ( !IsAlive() )
  	{
  		// dealing with a dead monster. 
  		if ( pev->max_health <= 0 )
  		{
  			// no blood decal for a monster that has already decalled its limit.
  			return; 
  		}
  		else
  		{
  			pev->max_health--;
  		}
  	}
  
  	for ( i = 0 ; i < cCount ; i++ )
  	{
  		vecTraceDir = vecDir;
  
  		vecTraceDir.x += RANDOM_FLOAT( -flNoise, flNoise );
  		vecTraceDir.y += RANDOM_FLOAT( -flNoise, flNoise );
  		vecTraceDir.z += RANDOM_FLOAT( -flNoise, flNoise );
  
  		UTIL_TraceLine( ptr->vecEndPos, ptr->vecEndPos + vecTraceDir * 172, ignore_monsters, ENT(pev), &Bloodtr);
  
  /*
  		MESSAGE_BEGIN( MSG_BROADCAST, SVC_TEMPENTITY );
  			WRITE_BYTE( TE_SHOWLINE);
  			WRITE_COORD( ptr->vecEndPos.x );
  			WRITE_COORD( ptr->vecEndPos.y );
  			WRITE_COORD( ptr->vecEndPos.z );
  			
  			WRITE_COORD( Bloodtr.vecEndPos.x );
  			WRITE_COORD( Bloodtr.vecEndPos.y );
  			WRITE_COORD( Bloodtr.vecEndPos.z );
  		MESSAGE_END();
  */
  
  		if ( Bloodtr.flFraction != 1.0 )
  		{
  			UTIL_BloodDecalTrace( &Bloodtr, BloodColor() );
  		}
  	}
  }
  