📄 npc_conscript.cpp
字号:
/***
*
* Copyright (c) 1999, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* This source code contains proprietary and confidential information of
* Valve LLC and its suppliers. Access to this code is restricted to
* persons who have executed a written SDK license with Valve. Any access,
* use or distribution of this code by or to any unlicensed person is illegal.
*
****/
//=========================================================
// monster template
//=========================================================
// UNDONE: Holster weapon?
#include "cbase.h"
#if 0
#include "npc_talker.h"
#include "ai_schedule.h"
#include "scripted.h"
#include "basecombatweapon.h"
#include "soundent.h"
#include "NPCEvent.h"
#include "AI_Hull.h"
#include "AI_Node.h"
#include "AI_Network.h"
#include "npc_conscript.h"
#include "activitylist.h"
#include "AI_Interactions.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
ConVar sk_conscript_health( "sk_conscript_health","0");
#define CONSCRIPT_MAD "CONSCRIPT_MAD"
#define CONSCRIPT_SHOT "CONSCRIPT_SHOT"
#define CONSCRIPT_KILL "CONSCRIPT_KILL"
#define CONSCRIPT_OUT_AMMO "CONSCRIPT_OUT_AMMO"
#define CONSCRIPT_ATTACK "CONSCRIPT_ATTACK"
#define CONSCRIPT_LINE_FIRE "CONSCRIPT_LINE_FIRE"
#define CONSCRIPT_POK "CONSCRIPT_POK"
#define CONSCRIPT_PAIN1 "CONSCRIPT_PAIN1"
#define CONSCRIPT_PAIN2 "CONSCRIPT_PAIN2"
#define CONSCRIPT_PAIN3 "CONSCRIPT_PAIN3"
#define CONSCRIPT_DIE1 "CONSCRIPT_DIE1"
#define CONSCRIPT_DIE2 "CONSCRIPT_DIE2"
#define CONSCRIPT_DIE3 "CONSCRIPT_DIE3"
//=========================================================
// Combine activities
//=========================================================
int ACT_CONSCRIPT_AIM;
//=========================================================
// Monster's Anim Events Go Here
//=========================================================
// first flag is barney dying for scripted sequences?
#define CONSCRIPT_AE_RELOAD ( 1 )
#define CONSCRIPT_AE_DRAW ( 2 )
#define CONSCRIPT_AE_SHOOT ( 3 )
#define CONSCRIPT_AE_HOLSTER ( 4 )
#define CONSCRIPT_BODY_GUNHOLSTERED 0
#define CONSCRIPT_BODY_GUNDRAWN 1
#define CONSCRIPT_BODY_GUNGONE 2
//-----------------------------------------------------------------------------
// Purpose: Initialize the custom schedules
// Input :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Conscript::InitCustomSchedules(void)
{
INIT_CUSTOM_AI(CNPC_Conscript);
ADD_CUSTOM_TASK(CNPC_Conscript, TASK_CONSCRIPT_CROUCH);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_FOLLOW);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_DRAW);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_FACE_TARGET);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_STAND);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_AIM);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_BARNACLE_HIT);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_BARNACLE_PULL);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_BARNACLE_CHOMP);
ADD_CUSTOM_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_BARNACLE_CHEW);
ADD_CUSTOM_ACTIVITY(CNPC_Combine, ACT_CONSCRIPT_AIM);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_FOLLOW);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_DRAW);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_FACE_TARGET);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_STAND);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_AIM);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_BARNACLE_HIT);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_BARNACLE_PULL);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_BARNACLE_CHOMP);
AI_LOAD_SCHEDULE(CNPC_Conscript, SCHED_CONSCRIPT_BARNACLE_CHEW);
}
LINK_ENTITY_TO_CLASS( npc_conscript, CNPC_Conscript );
IMPLEMENT_CUSTOM_AI( npc_conscript, CNPC_Conscript );
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_Conscript )
DEFINE_FIELD( CNPC_Conscript, m_fGunDrawn, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Conscript, m_painTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Conscript, m_checkAttackTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Conscript, m_nextLineFireTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Conscript, m_lastAttackCheck, FIELD_BOOLEAN ),
DEFINE_FIELD( CNPC_Conscript, m_bInBarnacleMouth, FIELD_BOOLEAN ),
END_DATADESC()
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Conscript::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;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
Activity CNPC_Conscript::NPC_TranslateActivity( Activity eNewActivity )
{
return BaseClass::NPC_TranslateActivity( eNewActivity );
}
//=========================================================
// GetSoundInterests - returns a bit mask indicating which types
// of sounds this monster regards.
//=========================================================
int CNPC_Conscript::GetSoundInterests ( void)
{
return SOUND_WORLD |
SOUND_COMBAT |
SOUND_CARCASS |
SOUND_MEAT |
SOUND_GARBAGE |
SOUND_DANGER |
SOUND_PLAYER;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
Class_T CNPC_Conscript::Classify ( void )
{
return CLASS_CONSCRIPT;
}
//=========================================================
// ALertSound - barney says "Freeze!"
//=========================================================
void CNPC_Conscript::AlertSound( void )
{
if ( GetEnemy() != NULL )
{
if ( IsOkToCombatSpeak() )
{
Speak( CONSCRIPT_ATTACK );
}
}
}
//=========================================================
// MaxYawSpeed - allows each sequence to have a different
// turn rate associated with it.
//=========================================================
float CNPC_Conscript::MaxYawSpeed ( void )
{
switch ( GetActivity() )
{
case ACT_IDLE:
return 70;
break;
case ACT_WALK:
return 70;
break;
case ACT_RUN:
return 90;
break;
default:
return 70;
break;
}
}
//-----------------------------------------------------------------------------
// Purpose: Set proper blend for shooting
// Input :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Conscript::ConscriptFirePistol ( void )
{
Vector vecShootOrigin;
vecShootOrigin = GetLocalOrigin() + Vector( 0, 0, 55 );
Vector vecShootDir = GetShootEnemyDir( vecShootOrigin );
QAngle angDir;
VectorAngles( vecShootDir, angDir );
SetPoseParameter( 0, angDir.x );
}
//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//
// Returns number of events handled, 0 if none.
//=========================================================
void CNPC_Conscript::HandleAnimEvent( animevent_t *pEvent )
{
switch( pEvent->event )
{
case CONSCRIPT_AE_RELOAD:
// We never actually run out of ammo, just need to refill the clip
if (GetActiveWeapon())
{
GetActiveWeapon()->WeaponSound( RELOAD_NPC );
GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1();
GetActiveWeapon()->m_iClip2 = GetActiveWeapon()->GetMaxClip2();
}
ClearCondition(COND_NO_PRIMARY_AMMO);
ClearCondition(COND_NO_SECONDARY_AMMO);
break;
case CONSCRIPT_AE_SHOOT:
ConscriptFirePistol();
break;
case CONSCRIPT_AE_DRAW:
// barney's bodygroup switches here so he can pull gun from holster
m_nBody = CONSCRIPT_BODY_GUNDRAWN;
m_fGunDrawn = true;
break;
case CONSCRIPT_AE_HOLSTER:
// change bodygroup to replace gun in holster
m_nBody = CONSCRIPT_BODY_GUNHOLSTERED;
m_fGunDrawn = false;
break;
default:
BaseClass::HandleAnimEvent( pEvent );
}
}
//=========================================================
// Spawn
//=========================================================
void CNPC_Conscript::Spawn()
{
Precache( );
SetModel( "models/conscript.mdl" );
SetHullType(HULL_HUMAN);
SetHullSizeNormal();
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_STEP );
SetBloodColor( BLOOD_COLOR_RED );
m_iHealth = sk_conscript_health.GetFloat();
SetViewOffset( Vector ( 0, 0, 50 ) );// 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;
m_nBody = 0; // gun in holster
m_fGunDrawn = false;
m_bInBarnacleMouth = false;
m_nextLineFireTime = 0;
m_HackedGunPos = Vector ( 0, 0, 55 );
CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_MOVE_GROUND | bits_CAP_MOVE_JUMP | bits_CAP_MOVE_CLIMB | bits_CAP_NO_HIT_PLAYER);
CapabilitiesAdd ( bits_CAP_USE_WEAPONS );
CapabilitiesAdd ( bits_CAP_DUCK ); // In reloading and cover
NPCInit();
SetUse( FollowerUse );
}
//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_Conscript::Precache()
{
engine->PrecacheModel("models/conscript.mdl");
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");
enginesound->PrecacheSound("barney/ba_close.wav");
// every new barney must call this, otherwise
// when a level is loaded, nobody will talk (time is reset to 0)
TalkInit();
BaseClass::Precache();
}
// Init talk data
void CNPC_Conscript::TalkInit()
{
BaseClass::TalkInit();
// vortigaunt will try to talk to friends in this order:
m_szFriends[0] = "npc_conscript";
// m_szFriends[1] = "npc_vortigaunt"; disable for now
// get voice for head - just one barney voice for now
GetExpresser()->SetVoicePitch( 100 );
}
static bool IsFacing( CBaseCombatCharacter *pBCC, const Vector &reference )
{
Vector vecDir = (reference - pBCC->GetLocalOrigin());
vecDir.z = 0;
VectorNormalize( vecDir );
Vector vecForward = pBCC->BodyDirection2D( );
// He's facing me, he meant it
if ( DotProduct( vecForward, vecDir ) > 0.96 ) // +/- 15 degrees or so
{
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output :
//-----------------------------------------------------------------------------
int CNPC_Conscript::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
// make sure friends talk about it if player hurts talkmonsters...
int ret = BaseClass::OnTakeDamage_Alive( info );
if (!IsAlive())
{
return ret;
}
if ( m_NPCState != NPC_STATE_PRONE && (info.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 I'm already suspicious, get mad
if (m_afMemory & bits_MEMORY_SUSPICIOUS)
{
// Alright, now I'm pissed!
Speak( CONSCRIPT_MAD );
Remember( bits_MEMORY_PROVOKED );
// Allowed to hit the player now!
CapabilitiesRemove(bits_CAP_NO_HIT_PLAYER);
StopFollowing();
}
else
{
// Hey, be careful with that
Speak( CONSCRIPT_SHOT );
Remember( bits_MEMORY_SUSPICIOUS );
}
}
else if ( !(GetEnemy()->IsPlayer()) && (m_lifeState != LIFE_DEAD ))
{
Speak( CONSCRIPT_SHOT );
}
}
return ret;
}
//------------------------------------------------------------------------------
// Purpose : Override to always shoot at eyes (for ducking behind things)
// Input :
// Output :
//------------------------------------------------------------------------------
Vector CNPC_Conscript::BodyTarget( const Vector &posSrc, bool bNoisy )
{
return EyePosition();
}
//=========================================================
// PainSound
//=========================================================
void CNPC_Conscript::PainSound ( void )
{
if (gpGlobals->curtime < m_painTime)
return;
AIConcept_t concepts[] =
{
CONSCRIPT_PAIN1,
CONSCRIPT_PAIN2,
CONSCRIPT_PAIN3,
};
m_painTime = gpGlobals->curtime + random->RandomFloat(0.5, 0.75);
Speak( concepts[random->RandomInt(0,2)] );
}
//=========================================================
// DeathSound
//=========================================================
void CNPC_Conscript::DeathSound ( void )
{
AIConcept_t concepts[] =
{
CONSCRIPT_DIE1,
CONSCRIPT_DIE2,
CONSCRIPT_DIE3,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -