You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
729 lines
17 KiB
729 lines
17 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
// $NoKeywords: $
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
#include "cbase.h"
|
||
|
#include "hl1_npc_talker.h"
|
||
|
#include "scripted.h"
|
||
|
#include "soundent.h"
|
||
|
#include "animation.h"
|
||
|
#include "entitylist.h"
|
||
|
#include "ai_navigator.h"
|
||
|
#include "ai_motor.h"
|
||
|
#include "player.h"
|
||
|
#include "vstdlib/random.h"
|
||
|
#include "engine/IEngineSound.h"
|
||
|
#include "npcevent.h"
|
||
|
#include "ai_interactions.h"
|
||
|
#include "doors.h"
|
||
|
|
||
|
#include "effect_dispatch_data.h"
|
||
|
#include "te_effect_dispatch.h"
|
||
|
#include "hl1_ai_basenpc.h"
|
||
|
#include "SoundEmitterSystem/isoundemittersystembase.h"
|
||
|
|
||
|
ConVar hl1_debug_sentence_volume( "hl1_debug_sentence_volume", "0" );
|
||
|
ConVar hl1_fixup_sentence_sndlevel( "hl1_fixup_sentence_sndlevel", "1" );
|
||
|
|
||
|
//#define TALKER_LOOK 0
|
||
|
|
||
|
BEGIN_DATADESC( CHL1NPCTalker )
|
||
|
|
||
|
DEFINE_ENTITYFUNC( Touch ),
|
||
|
DEFINE_FIELD( m_bInBarnacleMouth, FIELD_BOOLEAN ),
|
||
|
DEFINE_USEFUNC( FollowerUse ),
|
||
|
|
||
|
END_DATADESC()
|
||
|
|
||
|
void CHL1NPCTalker::RunTask( const Task_t *pTask )
|
||
|
{
|
||
|
switch ( pTask->iTask )
|
||
|
{
|
||
|
case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS:
|
||
|
{
|
||
|
float distance;
|
||
|
|
||
|
distance = (m_vecLastPosition - GetLocalOrigin()).Length2D();
|
||
|
|
||
|
// Walk path until far enough away
|
||
|
if ( distance > pTask->flTaskData ||
|
||
|
GetNavigator()->GetGoalType() == GOALTYPE_NONE )
|
||
|
{
|
||
|
TaskComplete();
|
||
|
GetNavigator()->ClearGoal(); // Stop moving
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
case TASK_TALKER_CLIENT_STARE:
|
||
|
case TASK_TALKER_LOOK_AT_CLIENT:
|
||
|
{
|
||
|
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
|
||
|
|
||
|
// track head to the client for a while.
|
||
|
if ( m_NPCState == NPC_STATE_IDLE &&
|
||
|
!IsMoving() &&
|
||
|
!GetExpresser()->IsSpeaking() )
|
||
|
{
|
||
|
|
||
|
if ( pPlayer )
|
||
|
{
|
||
|
IdleHeadTurn( pPlayer );
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// started moving or talking
|
||
|
TaskFail( "moved away" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( pTask->iTask == TASK_TALKER_CLIENT_STARE )
|
||
|
{
|
||
|
// fail out if the player looks away or moves away.
|
||
|
if ( ( pPlayer->GetAbsOrigin() - GetAbsOrigin() ).Length2D() > TALKER_STARE_DIST )
|
||
|
{
|
||
|
// player moved away.
|
||
|
TaskFail( NO_TASK_FAILURE );
|
||
|
}
|
||
|
|
||
|
Vector vForward;
|
||
|
AngleVectors( GetAbsAngles(), &vForward );
|
||
|
if ( UTIL_DotPoints( pPlayer->GetAbsOrigin(), GetAbsOrigin(), vForward ) < m_flFieldOfView )
|
||
|
{
|
||
|
// player looked away
|
||
|
TaskFail( "looked away" );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gpGlobals->curtime > m_flWaitFinished )
|
||
|
{
|
||
|
TaskComplete( NO_TASK_FAILURE );
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case TASK_WAIT_FOR_MOVEMENT:
|
||
|
{
|
||
|
if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
|
||
|
{
|
||
|
// ALERT(at_console, "walking, talking\n");
|
||
|
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
|
||
|
}
|
||
|
else if ( GetEnemy() )
|
||
|
{
|
||
|
IdleHeadTurn( GetEnemy() );
|
||
|
}
|
||
|
|
||
|
BaseClass::RunTask( pTask );
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case TASK_FACE_PLAYER:
|
||
|
{
|
||
|
CBasePlayer *pPlayer = UTIL_GetLocalPlayer();
|
||
|
|
||
|
if ( pPlayer )
|
||
|
{
|
||
|
//GetMotor()->SetIdealYaw( pPlayer->GetAbsOrigin() );
|
||
|
IdleHeadTurn( pPlayer );
|
||
|
if ( gpGlobals->curtime > m_flWaitFinished && GetMotor()->DeltaIdealYaw() < 10 )
|
||
|
{
|
||
|
TaskComplete();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TaskFail( FAIL_NO_PLAYER );
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case TASK_TALKER_EYECONTACT:
|
||
|
{
|
||
|
if (!IsMoving() && GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
|
||
|
{
|
||
|
// ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time );
|
||
|
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
|
||
|
}
|
||
|
|
||
|
BaseClass::RunTask( pTask );
|
||
|
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
|
||
|
{
|
||
|
IdleHeadTurn( GetSpeechTarget(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
|
||
|
}
|
||
|
else if ( GetEnemy() && m_NPCState == NPC_STATE_COMBAT )
|
||
|
{
|
||
|
IdleHeadTurn( GetEnemy() );
|
||
|
}
|
||
|
else if ( GetFollowTarget() )
|
||
|
{
|
||
|
IdleHeadTurn( GetFollowTarget() );
|
||
|
}
|
||
|
|
||
|
BaseClass::RunTask( pTask );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool CHL1NPCTalker::ShouldGib( const CTakeDamageInfo &info )
|
||
|
{
|
||
|
if ( info.GetDamageType() & DMG_NEVERGIB )
|
||
|
return false;
|
||
|
|
||
|
if ( ( g_pGameRules->Damage_ShouldGibCorpse( info.GetDamageType() ) && m_iHealth < GIB_HEALTH_VALUE ) || ( info.GetDamageType() & DMG_ALWAYSGIB ) )
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::StartTask( const Task_t *pTask )
|
||
|
{
|
||
|
switch( pTask->iTask )
|
||
|
{
|
||
|
case TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS:
|
||
|
{
|
||
|
GetNavigator()->SetMovementActivity( ACT_WALK );
|
||
|
break;
|
||
|
}
|
||
|
case TASK_TALKER_SPEAK:
|
||
|
// ask question or make statement
|
||
|
FIdleSpeak();
|
||
|
TaskComplete();
|
||
|
break;
|
||
|
default:
|
||
|
BaseClass::StartTask( pTask );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//=========================================================
|
||
|
// FIdleSpeak
|
||
|
// ask question of nearby friend, or make statement
|
||
|
//=========================================================
|
||
|
int CHL1NPCTalker::FIdleSpeak ( void )
|
||
|
{
|
||
|
if (!IsOkToSpeak())
|
||
|
return FALSE;
|
||
|
|
||
|
// if there is a friend nearby to speak to, play sentence, set friend's response time, return
|
||
|
// try to talk to any standing or sitting scientists nearby
|
||
|
CBaseEntity *pentFriend = FindNearestFriend( false );
|
||
|
CHL1NPCTalker *pentTalker = dynamic_cast<CHL1NPCTalker *>( pentFriend );
|
||
|
if (pentTalker && random->RandomInt(0,1) )
|
||
|
{
|
||
|
Speak( TLK_QUESTION );
|
||
|
SetSpeechTarget( pentFriend );
|
||
|
|
||
|
pentTalker->SetSpeechTarget( this );
|
||
|
pentTalker->SetCondition( COND_TALKER_RESPOND_TO_QUESTION );
|
||
|
pentTalker->SetSchedule( SCHED_TALKER_IDLE_RESPONSE );
|
||
|
pentTalker->GetExpresser()->BlockSpeechUntil( GetExpresser()->GetTimeSpeechComplete() );
|
||
|
|
||
|
GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + random->RandomFloat(4.8, 5.2) );
|
||
|
|
||
|
//DevMsg( "Asking some question!\n" );
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if ( random->RandomInt(0,1)) // otherwise, play an idle statement
|
||
|
{
|
||
|
//DevMsg( "Making idle statement!\n" );
|
||
|
|
||
|
Speak( TLK_IDLE );
|
||
|
// set global min delay for next conversation
|
||
|
GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + random->RandomFloat(4.8, 5.2) );
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// never spoke
|
||
|
GetExpresser()->BlockSpeechUntil( 0 );
|
||
|
m_flNextIdleSpeechTime = gpGlobals->curtime + 3;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
bool CHL1NPCTalker::IsValidSpeechTarget( int flags, CBaseEntity *pEntity )
|
||
|
{
|
||
|
if ( pEntity == this )
|
||
|
return false;
|
||
|
|
||
|
CHL1NPCTalker *pentTarget = dynamic_cast<CHL1NPCTalker *>( pEntity );
|
||
|
if ( pentTarget )
|
||
|
{
|
||
|
if ( !(flags & AIST_IGNORE_RELATIONSHIP) )
|
||
|
{
|
||
|
if ( pEntity->IsPlayer() )
|
||
|
{
|
||
|
if ( !IsPlayerAlly( (CBasePlayer *)pEntity ) )
|
||
|
return false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( IRelationType( pEntity ) != D_LI )
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !pEntity->IsAlive() )
|
||
|
// don't dead people
|
||
|
return false;
|
||
|
|
||
|
// Ignore no-target entities
|
||
|
if ( pEntity->GetFlags() & FL_NOTARGET )
|
||
|
return false;
|
||
|
|
||
|
CAI_BaseNPC *pNPC = pEntity->MyNPCPointer();
|
||
|
if ( pNPC )
|
||
|
{
|
||
|
// If not a NPC for some reason, or in a script.
|
||
|
//if ( (pNPC->m_NPCState == NPC_STATE_SCRIPT || pNPC->m_NPCState == NPC_STATE_PRONE))
|
||
|
// return false;
|
||
|
|
||
|
if ( pNPC->IsInAScript() )
|
||
|
return false;
|
||
|
|
||
|
// Don't bother people who don't want to be bothered
|
||
|
if ( !pNPC->CanBeUsedAsAFriend() )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if ( flags & AIST_FACING_TARGET )
|
||
|
{
|
||
|
if ( pEntity->IsPlayer() )
|
||
|
return HasCondition( COND_SEE_PLAYER );
|
||
|
else if ( !FInViewCone( pEntity ) )
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return FVisible( pEntity );
|
||
|
}
|
||
|
else
|
||
|
return BaseClass::IsValidSpeechTarget( flags, pEntity );
|
||
|
}
|
||
|
|
||
|
|
||
|
int CHL1NPCTalker::SelectSchedule ( void )
|
||
|
{
|
||
|
switch( m_NPCState )
|
||
|
{
|
||
|
case NPC_STATE_PRONE:
|
||
|
{
|
||
|
if (m_bInBarnacleMouth)
|
||
|
{
|
||
|
return SCHED_HL1TALKER_BARNACLE_CHOMP;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return SCHED_HL1TALKER_BARNACLE_HIT;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return BaseClass::SelectSchedule();
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::Precache()
|
||
|
{
|
||
|
BaseClass::Precache();
|
||
|
|
||
|
PrecacheScriptSound( "Barney.Close" );
|
||
|
}
|
||
|
|
||
|
bool CHL1NPCTalker::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
|
||
|
{
|
||
|
if (interactionType == g_interactionBarnacleVictimDangle)
|
||
|
{
|
||
|
// Force choosing of a new schedule
|
||
|
ClearSchedule( "NPC talker being eaten by a barnacle" );
|
||
|
m_bInBarnacleMouth = true;
|
||
|
return true;
|
||
|
}
|
||
|
else if ( interactionType == g_interactionBarnacleVictimReleased )
|
||
|
{
|
||
|
SetState ( NPC_STATE_IDLE );
|
||
|
|
||
|
CPASAttenuationFilter filter( this );
|
||
|
|
||
|
CSoundParameters params;
|
||
|
|
||
|
if ( GetParametersForSound( "Barney.Close", params, NULL ) )
|
||
|
{
|
||
|
EmitSound_t ep( params );
|
||
|
ep.m_nPitch = GetExpresser()->GetVoicePitch();
|
||
|
|
||
|
EmitSound( filter, entindex(), ep );
|
||
|
}
|
||
|
|
||
|
m_bInBarnacleMouth = false;
|
||
|
SetAbsVelocity( vec3_origin );
|
||
|
SetMoveType( MOVETYPE_STEP );
|
||
|
return true;
|
||
|
}
|
||
|
else if ( interactionType == g_interactionBarnacleVictimGrab )
|
||
|
{
|
||
|
if ( GetFlags() & FL_ONGROUND )
|
||
|
{
|
||
|
SetGroundEntity( NULL );
|
||
|
}
|
||
|
|
||
|
if ( GetState() == NPC_STATE_SCRIPT )
|
||
|
{
|
||
|
if ( m_hCine )
|
||
|
{
|
||
|
m_hCine->CancelScript();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SetState( NPC_STATE_PRONE );
|
||
|
ClearSchedule( "NPC talker grabbed by a barnacle" );
|
||
|
|
||
|
CTakeDamageInfo info;
|
||
|
PainSound( info );
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::StartFollowing( CBaseEntity *pLeader )
|
||
|
{
|
||
|
if ( !HasSpawnFlags( SF_NPC_GAG ) )
|
||
|
{
|
||
|
if ( m_iszUse != NULL_STRING )
|
||
|
{
|
||
|
PlaySentence( STRING( m_iszUse ), 0.0f );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Speak( TLK_STARTFOLLOW );
|
||
|
}
|
||
|
|
||
|
SetSpeechTarget( pLeader );
|
||
|
}
|
||
|
|
||
|
BaseClass::StartFollowing( pLeader );
|
||
|
}
|
||
|
|
||
|
int CHL1NPCTalker::PlayScriptedSentence( const char *pszSentence, float delay, float volume, soundlevel_t soundlevel, bool bConcurrent, CBaseEntity *pListener )
|
||
|
{
|
||
|
if( hl1_debug_sentence_volume.GetBool() )
|
||
|
{
|
||
|
Msg( "SENTENCE: %s Vol:%f SndLevel:%d\n", GetDebugName(), volume, soundlevel );
|
||
|
}
|
||
|
|
||
|
if( hl1_fixup_sentence_sndlevel.GetBool() )
|
||
|
{
|
||
|
if( soundlevel < SNDLVL_TALKING )
|
||
|
{
|
||
|
soundlevel = SNDLVL_TALKING;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return BaseClass::PlayScriptedSentence( pszSentence, delay, volume, soundlevel, bConcurrent, pListener );
|
||
|
}
|
||
|
|
||
|
Disposition_t CHL1NPCTalker::IRelationType( CBaseEntity *pTarget )
|
||
|
{
|
||
|
if ( pTarget->IsPlayer() )
|
||
|
{
|
||
|
if ( HasMemory( bits_MEMORY_PROVOKED ) )
|
||
|
{
|
||
|
return D_HT;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return BaseClass::IRelationType( pTarget );
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::Touch( CBaseEntity *pOther )
|
||
|
{
|
||
|
if ( m_NPCState == NPC_STATE_SCRIPT )
|
||
|
return;
|
||
|
|
||
|
BaseClass::Touch(pOther);
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::StopFollowing( void )
|
||
|
{
|
||
|
if ( !(m_afMemory & bits_MEMORY_PROVOKED) )
|
||
|
{
|
||
|
if ( !HasSpawnFlags( SF_NPC_GAG ) )
|
||
|
{
|
||
|
if ( m_iszUnUse != NULL_STRING )
|
||
|
{
|
||
|
PlaySentence( STRING( m_iszUnUse ), 0.0f );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Speak( TLK_STOPFOLLOW );
|
||
|
}
|
||
|
|
||
|
SetSpeechTarget( GetFollowTarget() );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BaseClass::StopFollowing();
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
||
|
{
|
||
|
if ( info.GetDamage() >= 1.0 && !(info.GetDamageType() & DMG_SHOCK ) )
|
||
|
{
|
||
|
UTIL_BloodImpact( ptr->endpos, vecDir, BloodColor(), 4 );
|
||
|
}
|
||
|
|
||
|
BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator );
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::FollowerUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||
|
{
|
||
|
// Don't allow use during a scripted_sentence
|
||
|
if ( GetUseTime() > gpGlobals->curtime )
|
||
|
return;
|
||
|
|
||
|
if ( m_hCine && !m_hCine->CanInterrupt() )
|
||
|
return;
|
||
|
|
||
|
if ( pCaller != NULL && pCaller->IsPlayer() )
|
||
|
{
|
||
|
// Pre-disaster followers can't be used
|
||
|
if ( m_spawnflags & SF_NPC_PREDISASTER )
|
||
|
{
|
||
|
SetSpeechTarget( pCaller );
|
||
|
DeclineFollowing();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
BaseClass::FollowerUse( pActivator, pCaller, useType, value );
|
||
|
}
|
||
|
|
||
|
int CHL1NPCTalker::TranslateSchedule( int scheduleType )
|
||
|
{
|
||
|
return BaseClass::TranslateSchedule( scheduleType );
|
||
|
}
|
||
|
|
||
|
float CHL1NPCTalker::PickLookTarget( bool bExcludePlayers, float minTime, float maxTime )
|
||
|
{
|
||
|
return random->RandomFloat( 5.0f, 10.0f );
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::IdleHeadTurn( CBaseEntity *pTarget, float flDuration, float flImportance )
|
||
|
{
|
||
|
// Must be able to turn our head
|
||
|
if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD))
|
||
|
return;
|
||
|
|
||
|
// If the target is invalid, or we're in a script, do nothing
|
||
|
if ( ( !pTarget ) || ( m_NPCState == NPC_STATE_SCRIPT ) )
|
||
|
return;
|
||
|
|
||
|
// Fill in a duration if we haven't specified one
|
||
|
if ( flDuration == 0.0f )
|
||
|
{
|
||
|
flDuration = random->RandomFloat( 2.0, 4.0 );
|
||
|
}
|
||
|
|
||
|
// Add a look target
|
||
|
AddLookTarget( pTarget, 1.0, flDuration );
|
||
|
}
|
||
|
|
||
|
void CHL1NPCTalker::SetHeadDirection( const Vector &vTargetPos, float flInterval)
|
||
|
{
|
||
|
#ifdef TALKER_LOOK
|
||
|
// Draw line in body, head, and eye directions
|
||
|
Vector vEyePos = EyePosition();
|
||
|
Vector vHeadDir = HeadDirection3D();
|
||
|
Vector vBodyDir = BodyDirection2D();
|
||
|
|
||
|
//UNDONE <<TODO>>
|
||
|
// currently eye dir just returns head dir, so use vTargetPos for now
|
||
|
//Vector vEyeDir; w
|
||
|
//EyeDirection3D(&vEyeDir);
|
||
|
NDebugOverlay::Line( vEyePos, vEyePos+(50*vHeadDir), 255, 0, 0, false, 0.1 );
|
||
|
NDebugOverlay::Line( vEyePos, vEyePos+(50*vBodyDir), 0, 255, 0, false, 0.1 );
|
||
|
NDebugOverlay::Line( vEyePos, vTargetPos, 0, 0, 255, false, 0.1 );
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
// Output : Returns true on success, false on failure.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CHL1NPCTalker::CorpseGib( const CTakeDamageInfo &info )
|
||
|
{
|
||
|
CEffectData data;
|
||
|
|
||
|
data.m_vOrigin = WorldSpaceCenter();
|
||
|
data.m_vNormal = data.m_vOrigin - info.GetDamagePosition();
|
||
|
VectorNormalize( data.m_vNormal );
|
||
|
|
||
|
data.m_flScale = RemapVal( m_iHealth, 0, -500, 1, 3 );
|
||
|
data.m_flScale = clamp( data.m_flScale, 1, 3 );
|
||
|
|
||
|
data.m_nMaterial = 1;
|
||
|
data.m_nHitBox = -m_iHealth;
|
||
|
|
||
|
data.m_nColor = BloodColor();
|
||
|
|
||
|
DispatchEffect( "HL1Gib", data );
|
||
|
|
||
|
CSoundEnt::InsertSound( SOUND_MEAT, GetAbsOrigin(), 256, 0.5f, this );
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// Purpose:
|
||
|
//-----------------------------------------------------------------------------
|
||
|
bool CHL1NPCTalker::OnObstructingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult )
|
||
|
{
|
||
|
// If we can't get through the door, try and open it
|
||
|
if ( BaseClass::OnObstructingDoor( pMoveGoal, pDoor, distClear, pResult ) )
|
||
|
{
|
||
|
if ( IsMoveBlocked( *pResult ) && pMoveGoal->directTrace.vHitNormal != vec3_origin )
|
||
|
{
|
||
|
// Can't do anything if the door's locked
|
||
|
if ( !pDoor->m_bLocked && !pDoor->HasSpawnFlags(SF_DOOR_NONPCS) )
|
||
|
{
|
||
|
// Tell the door to open
|
||
|
variant_t emptyVariant;
|
||
|
pDoor->AcceptInput( "Open", this, this, emptyVariant, USE_TOGGLE );
|
||
|
*pResult = AIMR_OK;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// HL1 version - never return Ragdoll as the automatic schedule at the end of a
|
||
|
// scripted sequence
|
||
|
int CHL1NPCTalker::SelectDeadSchedule()
|
||
|
{
|
||
|
// Alread dead (by animation event maybe?)
|
||
|
// Is it safe to set it to SCHED_NONE?
|
||
|
if ( m_lifeState == LIFE_DEAD )
|
||
|
return SCHED_NONE;
|
||
|
|
||
|
CleanupOnDeath();
|
||
|
return SCHED_DIE;
|
||
|
}
|
||
|
|
||
|
|
||
|
AI_BEGIN_CUSTOM_NPC( monster_hl1talker, CHL1NPCTalker )
|
||
|
|
||
|
DECLARE_TASK( TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS )
|
||
|
|
||
|
//=========================================================
|
||
|
// > SCHED_HL1TALKER_MOVE_AWAY_FOLLOW
|
||
|
//=========================================================
|
||
|
DEFINE_SCHEDULE
|
||
|
(
|
||
|
SCHED_HL1TALKER_FOLLOW_MOVE_AWAY,
|
||
|
|
||
|
" Tasks"
|
||
|
" TASK_SET_FAIL_SCHEDULE SCHEDULE:SCHED_TARGET_FACE"
|
||
|
" TASK_STORE_LASTPOSITION 0"
|
||
|
" TASK_MOVE_AWAY_PATH 100"
|
||
|
" TASK_HL1TALKER_FOLLOW_WALK_PATH_FOR_UNITS 100"
|
||
|
" TASK_STOP_MOVING 0"
|
||
|
" TASK_FACE_PLAYER 0"
|
||
|
" TASK_SET_ACTIVITY ACT_IDLE"
|
||
|
""
|
||
|
" Interrupts"
|
||
|
)
|
||
|
|
||
|
//=========================================================
|
||
|
// > SCHED_HL1TALKER_IDLE_SPEAK_WAIT
|
||
|
//=========================================================
|
||
|
DEFINE_SCHEDULE
|
||
|
(
|
||
|
SCHED_HL1TALKER_IDLE_SPEAK_WAIT,
|
||
|
|
||
|
" Tasks"
|
||
|
" TASK_SET_ACTIVITY ACTIVITY:ACT_IDLE" // Stop and talk
|
||
|
" TASK_FACE_PLAYER 0"
|
||
|
""
|
||
|
" Interrupts"
|
||
|
" COND_NEW_ENEMY"
|
||
|
" COND_LIGHT_DAMAGE"
|
||
|
" COND_HEAVY_DAMAGE"
|
||
|
" COND_HEAR_DANGER"
|
||
|
)
|
||
|
|
||
|
//=========================================================
|
||
|
// > SCHED_HL1TALKER_BARNACLE_HIT
|
||
|
//=========================================================
|
||
|
DEFINE_SCHEDULE
|
||
|
(
|
||
|
SCHED_HL1TALKER_BARNACLE_HIT,
|
||
|
|
||
|
" Tasks"
|
||
|
" TASK_STOP_MOVING 0"
|
||
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_HIT"
|
||
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HL1TALKER_BARNACLE_PULL"
|
||
|
""
|
||
|
" Interrupts"
|
||
|
)
|
||
|
|
||
|
//=========================================================
|
||
|
// > SCHED_HL1TALKER_BARNACLE_PULL
|
||
|
//=========================================================
|
||
|
DEFINE_SCHEDULE
|
||
|
(
|
||
|
SCHED_HL1TALKER_BARNACLE_PULL,
|
||
|
|
||
|
" Tasks"
|
||
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_PULL"
|
||
|
""
|
||
|
" Interrupts"
|
||
|
)
|
||
|
|
||
|
//=========================================================
|
||
|
// > SCHED_HL1TALKER_BARNACLE_CHOMP
|
||
|
//=========================================================
|
||
|
DEFINE_SCHEDULE
|
||
|
(
|
||
|
SCHED_HL1TALKER_BARNACLE_CHOMP,
|
||
|
|
||
|
" Tasks"
|
||
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHOMP"
|
||
|
" TASK_SET_SCHEDULE SCHEDULE:SCHED_HL1TALKER_BARNACLE_CHEW"
|
||
|
""
|
||
|
" Interrupts"
|
||
|
)
|
||
|
|
||
|
//=========================================================
|
||
|
// > SCHED_HL1TALKER_BARNACLE_CHEW
|
||
|
//=========================================================
|
||
|
DEFINE_SCHEDULE
|
||
|
(
|
||
|
SCHED_HL1TALKER_BARNACLE_CHEW,
|
||
|
|
||
|
" Tasks"
|
||
|
" TASK_PLAY_SEQUENCE ACTIVITY:ACT_BARNACLE_CHEW"
|
||
|
)
|
||
|
|
||
|
AI_END_CUSTOM_NPC()
|