📄 hl1_npc_barney.cpp
字号:
}
BaseClass::TraceAttack( info, vecDir, ptr );
}
void CNPC_Barney::Event_Killed( const CTakeDamageInfo &info )
{
if ( m_nBody < BARNEY_BODY_GUNGONE )
{
// drop the gun!
Vector vecGunPos;
QAngle angGunAngles;
CBaseEntity *pGun = NULL;
SetBodygroup( 1, BARNEY_BODY_GUNGONE);
GetAttachment( "0", vecGunPos, angGunAngles );
angGunAngles.y += 180;
pGun = DropItem( "weapon_glock", vecGunPos, angGunAngles );
}
SetUse( NULL );
BaseClass::Event_Killed( info );
}
void CNPC_Barney :: StartTask( const Task_t *pTask )
{
BaseClass::StartTask( pTask );
}
void CNPC_Barney :: RunTask( const Task_t *pTask )
{
switch ( pTask->iTask )
{
case TASK_RANGE_ATTACK1:
if (GetEnemy() != NULL && (GetEnemy()->IsPlayer()))
{
m_flPlaybackRate = 1.5;
}
BaseClass::RunTask( pTask );
break;
default:
BaseClass::RunTask( pTask );
break;
}
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CNPC_Barney :: HandleAnimEvent( animevent_t *pEvent )
{
switch( pEvent->event )
{
case BARNEY_AE_SHOOT:
BarneyFirePistol();
break;
case BARNEY_AE_DRAW:
// barney's bodygroup switches here so he can pull gun from holster
SetBodygroup( 1, BARNEY_BODY_GUNDRAWN);
m_fGunDrawn = true;
break;
case BARNEY_AE_HOLSTER:
// change bodygroup to replace gun in holster
SetBodygroup( 1, BARNEY_BODY_GUNHOLSTERED);
m_fGunDrawn = false;
break;
default:
BaseClass::HandleAnimEvent( pEvent );
}
}
//=========================================================
// AI Schedules Specific to this monster
//=========================================================
int CNPC_Barney::TranslateSchedule( int scheduleType )
{
switch( scheduleType )
{
case SCHED_ARM_WEAPON:
if ( GetEnemy() != NULL )
{
// face enemy, then draw.
return SCHED_BARNEY_ENEMY_DRAW;
}
break;
// 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_BARNEY_FACE_TARGET;
else
return baseType;
}
break;
case SCHED_TARGET_CHASE:
{
return SCHED_BARNEY_FOLLOW;
break;
}
case SCHED_IDLE_STAND:
{
int baseType;
// call base class default so that scientist will talk
// when 'used'
baseType = BaseClass::TranslateSchedule( scheduleType );
if ( baseType == SCHED_IDLE_STAND )
return SCHED_BARNEY_IDLE_STAND;
else
return baseType;
}
break;
case SCHED_TAKE_COVER_FROM_ENEMY:
case SCHED_CHASE_ENEMY:
{
if ( HasCondition( COND_HEAVY_DAMAGE ) )
return SCHED_TAKE_COVER_FROM_ENEMY;
// No need to take cover since I can see him
// SHOOT!
if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) && m_fGunDrawn )
return SCHED_RANGE_ATTACK1;
}
break;
}
return BaseClass::TranslateSchedule( scheduleType );
}
//=========================================================
// SelectSchedule - Decides which type of schedule best suits
// the monster's current state and conditions. Then calls
// monster's member function to get a pointer to a schedule
// of the proper type.
//=========================================================
int CNPC_Barney::SelectSchedule( void )
{
if ( GetFollowTarget() == NULL )
{
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 ) )
{
CSound *pSound;
pSound = GetBestSound();
ASSERT( pSound != NULL );
if ( pSound && pSound->IsSoundType( SOUND_DANGER ) )
return SCHED_TAKE_COVER_FROM_BEST_SOUND;
}
if ( HasCondition( COND_ENEMY_DEAD ) && IsOkToSpeak() )
{
Speak( BA_KILL );
}
switch( m_NPCState )
{
case NPC_STATE_COMBAT:
{
// dead enemy
if ( HasCondition( COND_ENEMY_DEAD ) )
return BaseClass::SelectSchedule(); // call base class, all code to handle dead enemies is centralized there.
// always act surprized with a new enemy
if ( HasCondition( COND_NEW_ENEMY ) && HasCondition( COND_LIGHT_DAMAGE) )
return SCHED_SMALL_FLINCH;
// wait for one schedule to draw gun
if (!m_fGunDrawn )
return SCHED_ARM_WEAPON;
if ( HasCondition( COND_HEAVY_DAMAGE ) )
return SCHED_TAKE_COVER_FROM_ENEMY;
if ( !HasCondition(COND_SEE_ENEMY) )
{
// we can't see the enemy
if ( !HasCondition(COND_ENEMY_OCCLUDED) )
{
// enemy is unseen, but not occluded!
// turn to face enemy
return SCHED_COMBAT_FACE;
}
else
{
return SCHED_CHASE_ENEMY;
}
}
}
break;
case NPC_STATE_ALERT:
case NPC_STATE_IDLE:
if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) )
{
// flinch if hurt
return SCHED_SMALL_FLINCH;
}
if ( GetEnemy() == NULL && GetFollowTarget() )
{
if ( !GetFollowTarget()->IsAlive() )
{
// UNDONE: Comment about the recently dead player here?
StopFollowing();
break;
}
else
{
return SCHED_TARGET_FACE;
}
}
// try to say something about smells
TrySmellTalk();
break;
}
return BaseClass::SelectSchedule();
}
NPC_STATE CNPC_Barney::SelectIdealState ( void )
{
return BaseClass::SelectIdealState();
}
void CNPC_Barney::DeclineFollowing( void )
{
Speak( BA_POK );
}
//------------------------------------------------------------------------------
//
// Schedules
//
//------------------------------------------------------------------------------
AI_BEGIN_CUSTOM_NPC( monster_barney, CNPC_Barney )
//=========================================================
// > SCHED_BARNEY_FOLLOW
//=========================================================
DEFINE_SCHEDULE
(
SCHED_BARNEY_FOLLOW,
" Tasks"
// " TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_BARNEY_STOP_FOLLOWING"
" TASK_GET_PATH_TO_TARGET 0"
" TASK_MOVE_TO_TARGET_RANGE 180"
" TASK_SET_SCHEDULE SCHEDULE:SCHED_TARGET_FACE"
" "
" Interrupts"
" COND_NEW_ENEMY"
" COND_LIGHT_DAMAGE"
" COND_HEAVY_DAMAGE"
" COND_HEAR_DANGER"
" COND_PROVOKED"
)
//=========================================================
// > SCHED_BARNEY_ENEMY_DRAW
//=========================================================
DEFINE_SCHEDULE
(
SCHED_BARNEY_ENEMY_DRAW,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_FACE_ENEMY 0"
" TASK_PLAY_SEQUENCE_FACE_ENEMY ACTIVITY:ACT_ARM"
" "
" Interrupts"
)
//=========================================================
// > SCHED_BARNEY_FACE_TARGET
//=========================================================
DEFINE_SCHEDULE
(
SCHED_BARNEY_FACE_TARGET,
" Tasks"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
" TASK_FACE_TARGET 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
" TASK_SET_SCHEDULE SCHEDULE:SCHED_BARNEY_FOLLOW"
" "
" Interrupts"
" COND_GIVE_WAY"
" COND_NEW_ENEMY"
" COND_LIGHT_DAMAGE"
" COND_HEAVY_DAMAGE"
" COND_PROVOKED"
" COND_HEAR_DANGER"
)
//=========================================================
// > SCHED_BARNEY_IDLE_STAND
//=========================================================
DEFINE_SCHEDULE
(
SCHED_BARNEY_IDLE_STAND,
" Tasks"
" TASK_STOP_MOVING 0"
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE"
" TASK_WAIT 2"
" TASK_TALKER_HEADRESET 0"
" "
" Interrupts"
" COND_NEW_ENEMY"
" COND_LIGHT_DAMAGE"
" COND_HEAVY_DAMAGE"
" COND_PROVOKED"
" COND_HEAR_COMBAT"
" COND_SMELL"
)
AI_END_CUSTOM_NPC()
//=========================================================
// DEAD BARNEY PROP
//
// Designer selects a pose in worldcraft, 0 through num_poses-1
// this value is added to what is selected as the 'first dead pose'
// among the monster's normal animations. All dead poses must
// appear sequentially in the model file. Be sure and set
// the m_iFirstPose properly!
//
//=========================================================
class CNPC_DeadBarney : public CAI_BaseNPC
{
DECLARE_CLASS( CNPC_DeadBarney, CAI_BaseNPC );
public:
void Spawn( void );
Class_T Classify ( void ) { return CLASS_NONE; }
void StartRagdoll( void );
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[3];
DECLARE_DATADESC();
};
char *CNPC_DeadBarney::m_szPoses[] = { "lying_on_back", "lying_on_side", "lying_on_stomach" };
bool CNPC_DeadBarney::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_barney_dead, CNPC_DeadBarney );
BEGIN_DATADESC( CNPC_DeadBarney )
// Function Pointers
DEFINE_THINKFUNC( CNPC_DeadBarney, StartRagdoll ),
END_DATADESC()
//=========================================================
// ********** DeadBarney SPAWN **********
//=========================================================
void CNPC_DeadBarney :: Spawn( void )
{
engine->PrecacheModel("models/barney.mdl");
SetModel( "models/barney.mdl");
m_fEffects = 0;
SetSequence( 0 );
m_bloodColor = BLOOD_COLOR_RED;
SetRenderColor( 255, 255, 255, 255 );
SetSequence( m_iDesiredSequence = LookupSequence( m_szPoses[m_iPose] ) );
if ( GetSequence() == -1 )
{
Msg ( "Dead barney with bad pose\n" );
}
// Corpses have less health
m_iHealth = 0.0;//gSkillData.barneyHealth;
NPCInit();
SetThink( StartRagdoll );
SetNextThink( gpGlobals->curtime + 0.1f );
}
void CNPC_DeadBarney::StartRagdoll( )
{
if ( GetSequence() == m_iDesiredSequence )
BecomeRagdollOnClient( Vector ( 0, 0, 0 ) );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -