📄 hl1_npc_scientist.cpp
字号:
}
}
return true;
}
return false;
}
//=========================================================
// PainSound
//=========================================================
void CNPC_Scientist::PainSound ( void )
{
if (gpGlobals->curtime < m_flPainTime )
return;
m_flPainTime = gpGlobals->curtime + random->RandomFloat( 0.5, 0.75 );
CPASAttenuationFilter filter( this );
switch (random->RandomInt( 0,4 ) )
{
case 0: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "scientist/sci_pain1.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
case 1: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "scientist/sci_pain2.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
case 2: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "scientist/sci_pain3.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
case 3: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "scientist/sci_pain4.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
case 4: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "scientist/sci_pain5.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
}
}
//=========================================================
// DeathSound
//=========================================================
void CNPC_Scientist::DeathSound ( void )
{
PainSound();
}
void CNPC_Scientist::Heal( void )
{
if ( !CanHeal() )
return;
Vector target = GetFollowTarget()->GetAbsOrigin() - GetAbsOrigin();
if ( target.Length() > 100 )
return;
GetTarget()->TakeHealth( sk_scientist_heal.GetFloat(), DMG_GENERIC );
// Don't heal again for 1 minute
m_flHealTime = gpGlobals->curtime + 60;
}
int CNPC_Scientist::TranslateSchedule( int scheduleType )
{
switch( scheduleType )
{
// Hook these to make a looping schedule
case SCHED_TARGET_FACE:
{
int baseType;
// call base class default so that scientist will talk
// when 'used'
baseType = BaseClass::TranslateSchedule( scheduleType );
if (baseType == SCHED_IDLE_STAND)
return SCHED_TARGET_FACE; // override this for different target face behavior
else
return baseType;
}
break;
case SCHED_TARGET_CHASE:
return SCHED_SCI_FOLLOWTARGET;
break;
case SCHED_IDLE_STAND:
{
int baseType;
baseType = BaseClass::TranslateSchedule( scheduleType );
if (baseType == SCHED_IDLE_STAND)
return SCHED_SCI_IDLESTAND; // override this for different target face behavior
else
return baseType;
}
break;
}
return BaseClass::TranslateSchedule( scheduleType );
}
Activity CNPC_Scientist::NPC_TranslateActivity( Activity newActivity )
{
if ( GetFollowTarget() && GetEnemy() )
{
CBaseEntity *pEnemy = GetEnemy();
int relationship = D_NU;
// Nothing scary, just me and the player
if ( pEnemy != NULL )
relationship = IRelationType( pEnemy );
if ( relationship == D_HT || relationship == D_FR )
{
if ( newActivity == ACT_WALK )
return ACT_WALK_SCARED;
else if ( newActivity == ACT_RUN )
return ACT_RUN_SCARED;
}
}
return BaseClass::NPC_TranslateActivity( newActivity );
}
int CNPC_Scientist::SelectSchedule( void )
{
// so we don't keep calling through the EHANDLE stuff
CBaseEntity *pEnemy = GetEnemy();
if ( GetFollowTarget() )
{
// so we don't keep calling through the EHANDLE stuff
CBaseEntity *pEnemy = GetEnemy();
int relationship = D_NU;
// Nothing scary, just me and the player
if ( pEnemy != NULL )
relationship = IRelationType( pEnemy );
if ( relationship != D_HT && relationship != D_FR )
{
// If I'm already close enough to my target
if ( TargetDistance() <= 128 )
{
if ( CanHeal() ) // Heal opportunistically
return SCHED_SCI_HEAL;
}
}
}
else if ( HasCondition( COND_PLAYER_PUSHING ) && !(GetSpawnFlags() & SF_NPC_PREDISASTER ) )
{ // Player wants me to move
return SCHED_HL1TALKER_FOLLOW_MOVE_AWAY;
}
if ( BehaviorSelectSchedule() )
{
return BaseClass::SelectSchedule();
}
if ( HasCondition( COND_HEAR_DANGER ) && m_NPCState != NPC_STATE_PRONE )
{
CSound *pSound;
pSound = GetBestSound();
if ( pSound && pSound->IsSoundType(SOUND_DANGER) )
return SCHED_TAKE_COVER_FROM_BEST_SOUND;
}
switch( m_NPCState )
{
case NPC_STATE_ALERT:
case NPC_STATE_IDLE:
if ( pEnemy )
{
if ( HasCondition( COND_SEE_ENEMY ) )
m_flFearTime = gpGlobals->curtime;
else if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert
{
SetEnemy( NULL );
pEnemy = NULL;
}
}
if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
{
// flinch if hurt
return SCHED_SMALL_FLINCH;
}
// Cower when you hear something scary
if ( HasCondition( COND_HEAR_DANGER ) || HasCondition( COND_HEAR_COMBAT ) )
{
CSound *pSound;
pSound = GetBestSound();
if ( pSound )
{
if ( pSound->IsSoundType(SOUND_DANGER | SOUND_COMBAT) )
{
if ( gpGlobals->curtime - m_flFearTime > 3 ) // Only cower every 3 seconds or so
{
m_flFearTime = gpGlobals->curtime; // Update last fear
return SCHED_SCI_STARTLE; // This will just duck for a second
}
}
}
}
if ( GetFollowTarget() )
{
if ( !GetFollowTarget()->IsAlive() )
{
// UNDONE: Comment about the recently dead player here?
StopFollowing();
break;
}
int relationship = D_NU;
// Nothing scary, just me and the player
if ( pEnemy != NULL )
relationship = IRelationType( pEnemy );
if ( relationship != D_HT )
{
return SCHED_TARGET_FACE; // Just face and follow.
}
else // UNDONE: When afraid, scientist won't move out of your way. Keep This? If not, write move away scared
{
if ( HasCondition( COND_NEW_ENEMY ) ) // I just saw something new and scary, react
return SCHED_SCI_FEAR; // React to something scary
return SCHED_SCI_FACETARGETSCARED; // face and follow, but I'm scared!
}
}
// try to say something about smells
TrySmellTalk();
break;
case NPC_STATE_COMBAT:
if ( HasCondition( COND_NEW_ENEMY ) )
return SCHED_SCI_FEAR; // Point and scream!
if ( HasCondition( COND_SEE_ENEMY ) )
return SCHED_SCI_COVER; // Take Cover
if ( HasCondition( COND_HEAR_COMBAT ) || HasCondition( COND_HEAR_DANGER ) )
return SCHED_TAKE_COVER_FROM_BEST_SOUND; // Cower and panic from the scary sound!
return SCHED_SCI_COVER; // Run & Cower
break;
}
return BaseClass::SelectSchedule();
}
NPC_STATE CNPC_Scientist::SelectIdealState ( void )
{
switch ( m_NPCState )
{
case NPC_STATE_ALERT:
case NPC_STATE_IDLE:
if ( HasCondition( COND_NEW_ENEMY ) )
{
if ( GetFollowTarget() && GetEnemy() )
{
int relationship = IRelationType( GetEnemy() );
if ( relationship != D_FR || relationship != D_HT && ( !HasCondition( COND_LIGHT_DAMAGE ) || !HasCondition( COND_HEAVY_DAMAGE ) ) )
{
// Don't go to combat if you're following the player
m_IdealNPCState = NPC_STATE_ALERT;
return m_IdealNPCState;
}
StopFollowing();
}
}
else if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
{
// Stop following if you take damage
if ( GetFollowTarget() )
StopFollowing();
}
break;
case NPC_STATE_COMBAT:
{
CBaseEntity *pEnemy = GetEnemy();
if ( pEnemy != NULL )
{
if ( DisregardEnemy( pEnemy ) ) // After 15 seconds of being hidden, return to alert
{
// Strip enemy when going to alert
m_IdealNPCState = NPC_STATE_ALERT;
SetEnemy( NULL );
return m_IdealNPCState;
}
// Follow if only scared a little
if ( GetFollowTarget() )
{
m_IdealNPCState = NPC_STATE_ALERT;
return m_IdealNPCState;
}
if ( HasCondition( COND_SEE_ENEMY ) )
{
m_flFearTime = gpGlobals->curtime;
m_IdealNPCState = NPC_STATE_COMBAT;
return m_IdealNPCState;
}
}
}
break;
}
return BaseClass::SelectIdealState();
}
int CNPC_Scientist::FriendNumber( int arrayNumber )
{
static int array[3] = { 1, 2, 0 };
if ( arrayNumber < 3 )
return array[ arrayNumber ];
return arrayNumber;
}
float CNPC_Scientist::TargetDistance( void )
{
// If we lose the player, or he dies, return a really large distance
if ( GetTarget() == NULL || !GetTarget()->IsAlive() )
return 1e6;
return (GetTarget()->GetLocalOrigin() - GetLocalOrigin()).Length();
}
//=========================================================
// Dead Scientist PROP
//=========================================================
class CNPC_DeadScientist : public CAI_BaseNPC
{
DECLARE_CLASS( CNPC_DeadScientist, CAI_BaseNPC );
public:
void Spawn( void );
Class_T Classify ( void ) { return CLASS_NONE; }
bool KeyValue( const char *szKeyName, const char *szValue );
float MaxYawSpeed ( void ) { return 8.0f; }
int m_iPose;// which sequence to display -- temporary, don't need to save
int m_iDesiredSequence;
static char *m_szPoses[7];
};
char *CNPC_DeadScientist::m_szPoses[] = { "lying_on_back", "lying_on_stomach", "dead_sitting", "dead_hang", "dead_table1", "dead_table2", "dead_table3" };
bool CNPC_DeadScientist::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "pose" ) )
m_iPose = atoi( szValue );
else
BaseClass::KeyValue( szKeyName, szValue );
return true;
}
LINK_ENTITY_TO_CLASS( monster_scientist_dead, CNPC_DeadScientist );
//
// ********** DeadScientist SPAWN **********
//
void CNPC_DeadScientist::Spawn( void )
{
engine->PrecacheModel("models/scientist.mdl");
SetModel( "models/scientist.mdl" );
m_fEffects = 0;
SetSequence( 0 );
m_bloodColor = BLOOD_COLOR_RED;
SetRenderColor( 255, 255, 255, 255 );
if ( m_nBody == -1 )
{// -1 chooses a random head
m_nBody = random->RandomInt( 0, NUM_SCIENTIST_HEADS-1);// pick a head, any head
}
// Luther is black, make his hands black
if ( m_nBody == HEAD_LUTHER )
m_nSkin = 1;
else
m_nSkin = 0;
SetSequence( LookupSequence( m_szPoses[m_iPose] ) );
if ( GetSequence() == -1)
{
Msg ( "Dead scientist with bad pose\n" );
}
m_iHealth = 0.0;//gSkillData.barneyHealth;
NPCInitDead();
}
//=========================================================
// Sitting Scientist PROP
//=========================================================
LINK_ENTITY_TO_CLASS( monster_sitting_scientist, CNPC_SittingScientist );
//IMPLEMENT_CUSTOM_AI( monster_sitting_scientist, CNPC_SittingScientist );
//IMPLEMENT_SERVERCLASS_ST( CNPC_SittingScientist, DT_NPC_SittingScientist )
//END_SEND_TABLE()
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_SittingScientist )
DEFINE_FIELD( CNPC_SittingScientist, m_iHeadTurn, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_SittingScientist, m_flResponseDelay, FIELD_FLOAT ),
DEFINE_THINKFUNC( CNPC_SittingScientist, SittingThink ),
END_DATADESC()
// animation sequence aliases
typedef enum
{
SITTING_ANIM_sitlookleft,
SITTING_ANIM_sitlookright,
SITTING_ANIM_sitscared,
SITTING_ANIM_sitting2,
SITTING_ANIM_sitting3
} SITTING_ANIM;
//
// ********** Scientist SPAWN **********
//
void CNPC_SittingScientist :: Spawn( )
{
engine->PrecacheModel("models/scientist.mdl");
SetModel("models/scientist.mdl");
Precache();
InitBoneControllers();
SetHullType(HULL_HUMAN);
SetHullSizeNormal();
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
m_fEffects = 0;
m_iHealth = 50;
m_bloodColor = BLOOD_COLOR_RED;
m_flFieldOfView = VIEW_FIELD_WIDE; // indicates the width of this monster's forward view cone ( as a dotproduct result )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -