📄 hl1_npc_hgrunt.cpp
字号:
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 + -