⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hl1_npc_hgrunt.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 5 页
字号:
	int squadCount;
	int iMyClass = Classify();// cache this monster's class

	if ( maxMembers < 2 )
		 return 0;

	// I am my own leader
	squadCount = 1;
	
	CBaseEntity *pEntity = NULL;

	if ( m_SquadName != NULL_STRING )
	{
		// I have a netname, so unconditionally recruit everyone else with that name.
		pEntity = gEntList.FindEntityByClassname( pEntity, "monster_human_grunt" );

		while ( pEntity )
		{
			CNPC_HGrunt *pRecruit = (CNPC_HGrunt*)pEntity->MyNPCPointer();

			if ( pRecruit )
			{
				if ( !pRecruit->m_pSquad && pRecruit->Classify() == iMyClass && pRecruit != this )
				{
					// minimum protection here against user error.in worldcraft. 
					if ( pRecruit->m_SquadName != NULL_STRING && FStrEq( STRING( m_SquadName ), STRING( pRecruit->m_SquadName ) ) )
					{
						pRecruit->InitSquad();
						squadCount++;
					}
				}
			}
	
			pEntity = gEntList.FindEntityByClassname( pEntity, "monster_human_grunt" );
		}

		return squadCount;
	}
	else
	{
		char szSquadName[64];
		Q_snprintf( szSquadName, sizeof( szSquadName ), "squad%d\n", g_iSquadIndex );

		m_SquadName = MAKE_STRING( szSquadName );

		while ( ( pEntity = gEntList.FindEntityInSphere( pEntity, GetAbsOrigin(), searchRadius ) ) != NULL )
		{
			if ( !FClassnameIs ( pEntity, "monster_human_grunt" ) )
				  continue;

			CNPC_HGrunt *pRecruit = (CNPC_HGrunt*)pEntity->MyNPCPointer();

			if ( pRecruit && pRecruit != this && pRecruit->IsAlive() && !pRecruit->m_hCine )
			{
				// Can we recruit this guy?
				if ( !pRecruit->m_pSquad && pRecruit->Classify() == iMyClass &&
				   ( (iMyClass != CLASS_ALIEN_MONSTER) || FClassnameIs( this, pRecruit->GetClassname() ) ) &&
					!pRecruit->m_SquadName )
				{
					trace_t tr;
					UTIL_TraceLine( GetAbsOrigin() + GetViewOffset(), pRecruit->GetAbsOrigin() + GetViewOffset(), MASK_NPCSOLID_BRUSHONLY, pRecruit, COLLISION_GROUP_NONE, &tr );// try to hit recruit with a traceline.

					if ( tr.fraction == 1.0 )
					{
						//We're ready to recruit people, so start a squad if I don't have one.
						if ( !m_pSquad )
						{
							InitSquad();
						}

						pRecruit->m_SquadName = m_SquadName;

						pRecruit->CapabilitiesAdd ( bits_CAP_SQUAD );
						pRecruit->InitSquad();

						squadCount++;
					}
				}
			}
		}

		if ( squadCount > 1 )
		{
			 g_iSquadIndex++;
		}
	}

	return squadCount;
}

void CNPC_HGrunt::StartNPC ( void )
{
	if ( !m_pSquad )
	{
		if ( m_SquadName != NULL_STRING )
		{
			// if I have a groupname, I can only recruit if I'm flagged as leader
			if ( GetSpawnFlags() & SF_GRUNT_LEADER )
			{
				InitSquad();

				// try to form squads now.
				int iSquadSize = SquadRecruit( 1024, 4 );

				if ( iSquadSize )
				{
				  Msg ( "Squad of %d %s formed\n", iSquadSize, GetClassname() );
				}
			}
			else
			{

				//Hacky.
				//Revisit me later.
				const char *pSquadName = STRING( m_SquadName );

				m_SquadName = NULL_STRING;

				BaseClass::StartNPC();

				m_SquadName = MAKE_STRING( pSquadName );

				return;
			}
		}
		else
		{
			int iSquadSize = SquadRecruit( 1024, 4 );

			if ( iSquadSize )
			{
			  Msg ( "Squad of %d %s formed\n", iSquadSize, GetClassname() );
			}
		}
	}

	BaseClass::StartNPC();

	if ( m_pSquad && m_pSquad->IsLeader( this ) )
	{
		SetBodygroup( 1, 1 ); // UNDONE: truly ugly hack
		m_nSkin = 0;
	}
}

//=========================================================
// CheckMeleeAttack1
//=========================================================
int CNPC_HGrunt::MeleeAttack1Conditions ( float flDot, float flDist )
{
	if (flDist > 64)
		return COND_TOO_FAR_TO_ATTACK;
	else if (flDot < 0.7)
		return COND_NOT_FACING_ATTACK;
	
	return COND_CAN_MELEE_ATTACK1;
}

//=========================================================
// CheckRangeAttack1 - overridden for HGrunt, cause 
// FCanCheckAttacks() doesn't disqualify all attacks based
// on whether or not the enemy is occluded because unlike
// the base class, the HGrunt can attack when the enemy is
// occluded (throw grenade over wall, etc). We must 
// disqualify the machine gun attack if the enemy is occluded.
//=========================================================
int CNPC_HGrunt::RangeAttack1Conditions ( float flDot, float flDist )
{
	if ( !HasCondition( COND_ENEMY_OCCLUDED ) && flDist <= 2048 && flDot >= 0.5 && NoFriendlyFire() )
	{
		trace_t	tr;

		if ( !GetEnemy()->IsPlayer() && flDist <= 64 )
		{
			// kick nonclients, but don't shoot at them.
			return COND_NONE;
		}

		Vector vecSrc;
		QAngle angAngles;

		GetAttachment( "0", vecSrc, angAngles );

		//NDebugOverlay::Line( GetAbsOrigin() + GetViewOffset(), GetEnemy()->BodyTarget(GetAbsOrigin() + GetViewOffset()), 255, 0, 0, false, 0.1 );
		// verify that a bullet fired from the gun will hit the enemy before the world.
		UTIL_TraceLine( GetAbsOrigin() + GetViewOffset(), GetEnemy()->BodyTarget(GetAbsOrigin() + GetViewOffset()), MASK_SHOT, this/*pentIgnore*/, COLLISION_GROUP_NONE, &tr);

		if ( tr.m_pEnt == GetEnemy() )
		{
			return COND_CAN_RANGE_ATTACK1;
		}
	}

	if ( !NoFriendlyFire() )
		 return COND_WEAPON_BLOCKED_BY_FRIEND; //err =|

	return COND_NONE;
}

int CNPC_HGrunt::RangeAttack2Conditions( float flDot, float flDist  )
{
	m_iLastGrenadeCondition = GetGrenadeConditions( flDot, flDist );
	return m_iLastGrenadeCondition;
}

int CNPC_HGrunt::GetGrenadeConditions( float flDot, float flDist  )
{
	if ( !FBitSet( m_iWeapons, ( HGRUNT_HANDGRENADE | HGRUNT_GRENADELAUNCHER ) ) )
		  return COND_NONE;

	// assume things haven't changed too much since last time
	if (gpGlobals->curtime < m_flNextGrenadeCheck )
		return m_iLastGrenadeCondition;
	
	if ( m_flGroundSpeed != 0 )
		return COND_NONE;

	CBaseEntity *pEnemy = GetEnemy();
	
	if (!pEnemy)
		return COND_NONE;
	
	Vector flEnemyLKP = GetEnemyLKP();
	if ( !(pEnemy->GetFlags() & FL_ONGROUND) && pEnemy->GetWaterLevel() == 0 && flEnemyLKP.z > GetAbsMaxs().z  )
	{
		//!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to 
		// be grenaded.
		// don't throw grenades at anything that isn't on the ground!
		return COND_NONE;
	}
	
	Vector vecTarget;

	if (FBitSet( m_iWeapons, HGRUNT_HANDGRENADE))
	{
		// find feet
		if ( random->RandomInt( 0,1 ) )
		{
			// magically know where they are
			vecTarget = Vector( pEnemy->GetAbsOrigin().x, pEnemy->GetAbsOrigin().y, pEnemy->GetAbsMins().z );
		}
		else
		{
			// toss it to where you last saw them
			vecTarget = flEnemyLKP;
		}
	
	}
	else
	{
		// find target
		// vecTarget = GetEnemy()->BodyTarget( GetAbsOrigin() );
		vecTarget = GetEnemy()->GetAbsOrigin() + (GetEnemy()->BodyTarget( GetAbsOrigin() ) - GetEnemy()->GetAbsOrigin());
		// estimate position
		if ( HasCondition( COND_SEE_ENEMY))
			vecTarget = vecTarget + ((vecTarget - GetAbsOrigin()).Length() / sk_hgrunt_gspeed.GetFloat()) * GetEnemy()->GetAbsVelocity();
	}

	// are any of my squad members near the intended grenade impact area?
	if ( m_pSquad )
	{
		if ( m_pSquad->SquadMemberInRange( vecTarget, 256 ) )
		{
			// crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
			return COND_NONE;
		}
	}
	
	if ( ( vecTarget - GetAbsOrigin() ).Length2D() <= 256 )
	{
		// crap, I don't want to blow myself up
		m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.
		return COND_NONE;
	}

		
	if (FBitSet( m_iWeapons, HGRUNT_HANDGRENADE))
	{
		Vector vGunPos;
		QAngle angGunAngles;
		GetAttachment( "0", vGunPos, angGunAngles );


		Vector vecToss = VecCheckToss( this, vGunPos, vecTarget, -1, 0.5, false );

		if ( vecToss != vec3_origin )
		{
			m_vecTossVelocity = vecToss;

			// don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second.

			return COND_CAN_RANGE_ATTACK2;
		}
		else
		{
			// don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.

			return COND_NONE;
		}
	}
	else
	{
		Vector vGunPos;
		QAngle angGunAngles;
		GetAttachment( "0", vGunPos, angGunAngles );
		
		Vector vecToss = VecCheckThrow( this, vGunPos, vecTarget, sk_hgrunt_gspeed.GetFloat(), 0.5 );

		if ( vecToss != vec3_origin )
		{
			m_vecTossVelocity = vecToss;

			// don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second.

			return COND_CAN_RANGE_ATTACK2;
		}
		else
		{
			// don't check again for a while.
			m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second.

			return COND_NONE;
		}
	}
}


//=========================================================
// FCanCheckAttacks - this is overridden for human grunts
// because they can throw/shoot grenades when they can't see their
// target and the base class doesn't check attacks if the monster
// cannot see its enemy.
//
// !!!BUGBUG - this gets called before a 3-round burst is fired
// which means that a friendly can still be hit with up to 2 rounds. 
// ALSO, grenades will not be tossed if there is a friendly in front,
// this is a bad bug. Friendly machine gun fire avoidance
// will unecessarily prevent the throwing of a grenade as well.
//=========================================================
bool CNPC_HGrunt::FCanCheckAttacks( void )
{
	// This condition set when too close to a grenade to blow it up
	if ( !HasCondition( COND_TOO_CLOSE_TO_ATTACK ) )
	{
		return true;
	}
	else
	{
		return false;
	}
}

int CNPC_HGrunt::GetSoundInterests( void )
{
	return	SOUND_WORLD	|
			SOUND_COMBAT	|
			SOUND_PLAYER	|
			SOUND_DANGER;
}


//=========================================================
// TraceAttack - make sure we're not taking it in the helmet
//=========================================================
void CNPC_HGrunt::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr )
{
	CTakeDamageInfo info = inputInfo;

	// check for helmet shot
	if (ptr->hitgroup == 11)
	{
		// make sure we're wearing one
		if ( GetBodygroup( 1 ) == HEAD_GRUNT && (info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_BLAST | DMG_CLUB)))
		{
			// absorb damage
			info.SetDamage( info.GetDamage() - 20 );
			if ( info.GetDamage() <= 0 )
				info.SetDamage( 0.01 );
		}
		// it's head shot anyways
		ptr->hitgroup = HITGROUP_HEAD;
	}
	BaseClass::TraceAttack( info, vecDir, ptr );
}


//=========================================================
// TakeDamage - overridden for the grunt because the grunt
// needs to forget that he is in cover if he's hurt. (Obviously
// not in a safe place anymore).
//=========================================================
int CNPC_HGrunt::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
{
	Forget( bits_MEMORY_INCOVER );

	return BaseClass::OnTakeDamage_Alive ( inputInfo );
}


//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
float CNPC_HGrunt::MaxYawSpeed( void )
{
	float flYS;

	switch ( GetActivity() )
	{
	case ACT_IDLE:	
		flYS = 150;		
		break;
	case ACT_RUN:	
		flYS = 150;	
		break;
	case ACT_WALK:	
		flYS = 180;		
		break;
	case ACT_RANGE_ATTACK1:	
		flYS = 120;	
		break;
	case ACT_RANGE_ATTACK2:	
		flYS = 120;	
		break;
	case ACT_MELEE_ATTACK1:	
		flYS = 120;	
		break;
	case ACT_MELEE_ATTACK2:	
		flYS = 120;	
		break;
	case ACT_TURN_LEFT:
	case ACT_TURN_RIGHT:	
		flYS = 180;
		break;
	case ACT_GLIDE:
	case ACT_FLY:
		flYS = 30;
		break;
	default:
		flYS = 90;
		break;
	}

	return flYS;
}

void CNPC_HGrunt::IdleSound( void )
{
	if (FOkToSpeak() && ( g_fGruntQuestion || random->RandomInt( 0,1 ) ) )
	{
		if (!g_fGruntQuestion)
		{
			// ask question or make statement
			switch ( random->RandomInt( 0,2 ) )
			{
			case 0: // check in
				SENTENCEG_PlayRndSz( edict(), "HG_CHECK", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
				g_fGruntQuestion = 1;
				break;
			case 1: // question
				SENTENCEG_PlayRndSz( edict(), "HG_QUEST", HGRUNT_SENTENCE_VOLUME, SNDLVL_NORM, 0, m_voicePitch);
				g_fGruntQuestion = 2;
				break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -