📄 hl1_npc_barney.cpp
字号:
//=========== (C) Copyright 2000 Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: Bullseyes act as targets for other NPC's to attack and to trigger
// events
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#include "cbase.h"
#include "AI_Default.h"
#include "AI_Task.h"
#include "AI_Schedule.h"
#include "AI_Node.h"
#include "AI_Hull.h"
#include "AI_Hint.h"
#include "AI_Memory.h"
#include "AI_Route.h"
#include "AI_Motor.h"
#include "hl1_npc_barney.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "EntityList.h"
#include "activitylist.h"
#include "animation.h"
#include "basecombatweapon.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "ammodef.h"
#include "AI_Behavior_Follow.h"
#include "AI_Criteria.h"
#define BA_ATTACK "BA_ATTACK"
#define BA_MAD "BA_MAD"
#define BA_SHOT "BA_SHOT"
#define BA_KILL "BA_KILL"
#define BA_POK "BA_POK"
ConVar sk_barney_health( "sk_barney_health","35");
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
// first flag is barney dying for scripted sequences?
#define BARNEY_AE_DRAW ( 2 )
#define BARNEY_AE_SHOOT ( 3 )
#define BARNEY_AE_HOLSTER ( 4 )
#define BARNEY_BODY_GUNHOLSTERED 0
#define BARNEY_BODY_GUNDRAWN 1
#define BARNEY_BODY_GUNGONE 2
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_Barney )
DEFINE_FIELD( CNPC_Barney, m_fGunDrawn, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Barney, m_flPainTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Barney, m_flCheckAttackTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Barney, m_fLastAttackCheck, FIELD_BOOLEAN ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( monster_barney, CNPC_Barney );
static BOOL IsFacing( CBaseEntity *pevTest, const Vector &reference )
{
Vector vecDir = (reference - pevTest->GetAbsOrigin());
vecDir.z = 0;
VectorNormalize( vecDir );
Vector forward;
QAngle angle;
angle = pevTest->GetAbsAngles();
angle.x = 0;
AngleVectors( angle, &forward );
// He's facing me, he meant it
if ( DotProduct( forward, vecDir ) > 0.96 ) // +/- 15 degrees or so
{
return TRUE;
}
return FALSE;
}
//=========================================================
// Spawn
//=========================================================
void CNPC_Barney::Spawn()
{
Precache( );
SetModel( "models/barney.mdl");
SetRenderColor( 255, 255, 255, 255 );
SetHullType(HULL_HUMAN);
SetHullSizeNormal();
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
m_bloodColor = BLOOD_COLOR_RED;
m_iHealth = sk_barney_health.GetFloat();
SetViewOffset( Vector ( 0, 0, 100 ) );// position of the eyes relative to monster's origin.
m_flFieldOfView = VIEW_FIELD_WIDE; // NOTE: we need a wide field of view so npc will notice player and say hello
m_NPCState = NPC_STATE_NONE;
SetBodygroup( 1, 0 );
m_fGunDrawn = false;
CapabilitiesClear();
CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE | bits_CAP_DOORS_GROUP);
CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 | bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );
NPCInit();
SetUse( FollowerUse );
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_Barney::Precache()
{
m_iAmmoType = GetAmmoDef()->Index("9mmRound");
engine->PrecacheModel("models/barney.mdl");
enginesound->PrecacheSound("barney/ba_attack1.wav" );
enginesound->PrecacheSound("barney/ba_attack2.wav" );
enginesound->PrecacheSound("barney/ba_pain1.wav");
enginesound->PrecacheSound("barney/ba_pain2.wav");
enginesound->PrecacheSound("barney/ba_pain3.wav");
enginesound->PrecacheSound("barney/ba_die1.wav");
enginesound->PrecacheSound("barney/ba_die2.wav");
enginesound->PrecacheSound("barney/ba_die3.wav");
// every new barney must call this, otherwise
// when a level is loaded, nobody will talk (time is reset to 0)
TalkInit();
BaseClass::Precache();
}
void CNPC_Barney::ModifyOrAppendCriteria( AI_CriteriaSet& set )
{
BaseClass::ModifyOrAppendCriteria( set );
bool predisaster = FBitSet( m_spawnflags, SF_NPC_PREDISASTER ) ? true : false;
set.AppendCriteria( "disaster", predisaster ? "[disaster::pre]" : "[disaster::post]" );
}
// Init talk data
void CNPC_Barney::TalkInit()
{
BaseClass::TalkInit();
// get voice for head - just one barney voice for now
GetExpresser()->SetVoicePitch( 100 );
}
//=========================================================
// GetSoundInterests - returns a bit mask indicating which types
// of sounds this monster regards.
//=========================================================
int CNPC_Barney::GetSoundInterests ( void)
{
return SOUND_WORLD |
SOUND_COMBAT |
SOUND_CARCASS |
SOUND_MEAT |
SOUND_GARBAGE |
SOUND_DANGER |
SOUND_PLAYER;
}
//=========================================================
// Classify - indicates this monster's place in the
// relationship table.
//=========================================================
Class_T CNPC_Barney::Classify ( void )
{
return CLASS_PLAYER_ALLY;
}
//=========================================================
// ALertSound - barney says "Freeze!"
//=========================================================
void CNPC_Barney::AlertSound( void )
{
if ( GetEnemy() != NULL )
{
if ( IsOkToSpeak() )
{
Speak( BA_ATTACK );
}
}
}
//=========================================================
// SetYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
void CNPC_Barney::SetYawSpeed ( void )
{
int ys;
ys = 0;
switch ( GetActivity() )
{
case ACT_IDLE:
ys = 70;
break;
case ACT_WALK:
ys = 70;
break;
case ACT_RUN:
ys = 90;
break;
default:
ys = 70;
break;
}
GetMotor()->SetYawSpeed( ys );
}
//=========================================================
// CheckRangeAttack1
//=========================================================
bool CNPC_Barney :: CheckRangeAttack1 ( float flDot, float flDist )
{
if ( gpGlobals->curtime > m_flCheckAttackTime )
{
trace_t tr;
Vector shootOrigin = GetAbsOrigin() + Vector( 0, 0, 55 );
CBaseEntity *pEnemy = GetEnemy();
Vector shootTarget = ( (pEnemy->BodyTarget( shootOrigin ) - pEnemy->GetAbsOrigin()) + GetEnemyLKP() );
UTIL_TraceLine ( shootOrigin, shootTarget, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
m_flCheckAttackTime = gpGlobals->curtime + 1;
if ( tr.fraction == 1.0 || ( tr.m_pEnt != NULL && tr.m_pEnt == pEnemy) )
m_fLastAttackCheck = TRUE;
else
m_fLastAttackCheck = FALSE;
m_flCheckAttackTime = gpGlobals->curtime + 1.5;
}
return m_fLastAttackCheck;
}
//------------------------------------------------------------------------------
// Purpose : For innate range attack
// Input :
// Output :
//------------------------------------------------------------------------------
int CNPC_Barney::RangeAttack1Conditions( float flDot, float flDist )
{
if (GetEnemy() == NULL)
{
return( COND_NONE );
}
else if ( flDist > 1024 )
{
return( COND_TOO_FAR_TO_ATTACK );
}
else if ( flDot < 0.5 )
{
return( COND_NOT_FACING_ATTACK );
}
if ( CheckRangeAttack1 ( flDot, flDist ) )
return( COND_CAN_RANGE_ATTACK1 );
return COND_NONE;
}
//=========================================================
// BarneyFirePistol - shoots one round from the pistol at
// the enemy barney is facing.
//=========================================================
void CNPC_Barney::BarneyFirePistol ( void )
{
Vector vecShootOrigin;
vecShootOrigin = GetAbsOrigin() + Vector( 0, 0, 55 );
Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
QAngle angDir;
VectorAngles( vecShootDir, angDir );
// SetBlending( 0, angDir.x );
m_fEffects = EF_MUZZLEFLASH;
FireBullets(1, vecShootOrigin, vecShootDir, VECTOR_CONE_2DEGREES, 1024, m_iAmmoType );
int pitchShift = random->RandomInt( 0, 20 );
// Only shift about half the time
if ( pitchShift > 10 )
pitchShift = 0;
else
pitchShift -= 5;
CPASAttenuationFilter filter( this );
enginesound->EmitSound( filter, entindex(), CHAN_WEAPON, "barney/ba_attack2.wav", 1, ATTN_NORM, 0, 100 + pitchShift );
CSoundEnt::InsertSound ( SOUND_COMBAT, GetAbsOrigin(), 384, 0.3 );
// UNDONE: Reload?
m_cAmmoLoaded--;// take away a bullet!
}
int CNPC_Barney::OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
{
// make sure friends talk about it if player hurts talkmonsters...
int ret = BaseClass::OnTakeDamage_Alive( inputInfo );
if ( !IsAlive() || m_lifeState == LIFE_DYING )
return ret;
if ( m_NPCState != NPC_STATE_PRONE && ( inputInfo.GetAttacker()->GetFlags() & FL_CLIENT ) )
{
// This is a heurstic to determine if the player intended to harm me
// If I have an enemy, we can't establish intent (may just be crossfire)
if ( GetEnemy() == NULL )
{
// If the player was facing directly at me, or I'm already suspicious, get mad
if ( HasMemory( bits_MEMORY_SUSPICIOUS ) || IsFacing( inputInfo.GetAttacker(), GetAbsOrigin() ) )
{
// Alright, now I'm pissed!
Speak( BA_MAD );
Remember( bits_MEMORY_PROVOKED );
StopFollowing();
}
else
{
// Hey, be careful with that
Speak( BA_SHOT );
Remember( bits_MEMORY_SUSPICIOUS );
}
}
else if ( !(GetEnemy()->IsPlayer()) && m_lifeState == LIFE_ALIVE )
{
Speak( BA_SHOT );
}
}
return ret;
}
//=========================================================
// PainSound
//=========================================================
void CNPC_Barney :: 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, 2 ) )
{
case 0: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "barney/ba_pain1.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
case 1: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "barney/ba_pain2.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
case 2: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "barney/ba_pain3.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
}
}
//=========================================================
// DeathSound
//=========================================================
void CNPC_Barney :: DeathSound ( void )
{
CPASAttenuationFilter filter( this );
switch ( random->RandomInt( 0, 2 ) )
{
case 0: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "barney/ba_die1.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
case 1: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "barney/ba_die2.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
case 2: enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "barney/ba_die3.wav", 1, ATTN_NORM, 0, GetExpresser()->GetVoicePitch()); break;
}
}
void CNPC_Barney::TraceAttack( const CTakeDamageInfo &inputInfo, const Vector &vecDir, trace_t *ptr )
{
CTakeDamageInfo info = inputInfo;
switch( ptr->hitgroup )
{
case HITGROUP_CHEST:
case HITGROUP_STOMACH:
if ( info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_BLAST) )
{
info.ScaleDamage( 0.5f );
}
break;
case 10:
if ( info.GetDamageType() & (DMG_BULLET | DMG_SLASH | DMG_CLUB) )
{
info.SetDamage( info.GetDamage() - 20 );
if ( info.GetDamage() <= 0 )
{
g_pEffects->Ricochet( ptr->endpos, ptr->plane.normal );
info.SetDamage( 0.01 );
}
}
// always a head shot
ptr->hitgroup = HITGROUP_HEAD;
break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -