⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 hl1_npc_talker.cpp

📁 hl2 source code. Do not use it illegal.
💻 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.
*
****/
#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 "effect_dispatch_data.h"
#include "te_effect_dispatch.h"
#include "hl1_ai_basenpc.h"

//#define TALKER_LOOK 0

BEGIN_DATADESC( CHL1NPCTalker )

	DEFINE_ENTITYFUNC( CHL1NPCTalker, Touch ),
	DEFINE_FIELD( CHL1NPCTalker, m_bInBarnacleMouth,	FIELD_BOOLEAN ),

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;
			
			pPlayer = (CBasePlayer *)CBaseEntity::Instance( engine->PEntityOfEntIndex( 1 ) );

			// track head to the client for a while.
			if ( m_NPCState == NPC_STATE_IDLE		&& 
				 !IsMoving()								&&
				 !GetExpresser()->IsSpeaking() )
			{
			
				if ( pPlayer )
				{
					IdleHeadTurn( pPlayer->GetAbsOrigin() );
				}
			}
			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() > TLK_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()->GetAbsOrigin(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
			}
			else if ( GetEnemy() )
			{
				IdleHeadTurn( GetEnemy()->GetAbsOrigin() );
			}
			else
				IdleHeadTurn( vec3_origin );

			BaseClass::RunTask( pTask );

			if ( TaskIsComplete() )
				 IdleHeadTurn( vec3_origin );

			break;
		}

		case TASK_FACE_PLAYER:
		{
			CBasePlayer *pPlayer;
			
			pPlayer = (CBasePlayer *)CBaseEntity::Instance( engine->PEntityOfEntIndex( 1 ) );

			if ( pPlayer )
			{
				//GetMotor()->SetIdealYaw( pPlayer->GetAbsOrigin() );
				IdleHeadTurn( pPlayer->GetAbsOrigin() );
				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()->GetAbsOrigin(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
			}
			
			BaseClass::RunTask( pTask );
			
			break;

		}

				
		default:
		{
		
			if ( GetExpresser()->IsSpeaking() && GetSpeechTarget() != NULL)
			{
				IdleHeadTurn( GetSpeechTarget()->GetAbsOrigin(), GetExpresser()->GetTimeSpeechComplete() - gpGlobals->curtime );
			}
			else if ( GetFollowTarget() )
			{
				IdleHeadTurn( GetFollowTarget()->GetAbsOrigin() );
			}
			else if ( GetEnemy() && m_NPCState == NPC_STATE_COMBAT )
			{
				IdleHeadTurn( GetEnemy()->GetAbsOrigin() );
			}
			else
			{
				IdleHeadTurn( vec3_origin );
			}

			BaseClass::RunTask( pTask );
			break;
		}
	}
}

bool CHL1NPCTalker::ShouldGib( const CTakeDamageInfo &info )
{
	if ( info.GetDamageType() & DMG_NEVERGIB )
		 return false;

	if ( ( info.GetDamageType() & DMG_GIB_CORPSE && 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;
		}
		default:
			BaseClass::StartTask( pTask );
			break;
	}
}

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();
}

bool CHL1NPCTalker::HandleInteraction(int interactionType, void *data, CBaseCombatCharacter* sourceEnt)
{
	if (interactionType == g_interactionBarnacleVictimDangle)
	{
		// Force choosing of a new schedule
		ClearSchedule();
		m_bInBarnacleMouth	= true;
		return true;
	}
	else if ( interactionType == g_interactionBarnacleVictimReleased )
	{
		SetState ( NPC_STATE_IDLE );

		CPASAttenuationFilter filter( this );
		EmitSound( filter, entindex(), CHAN_VOICE, "barney/ba_close.wav", 1, SNDLVL_TALKING, 0, GetExpresser()->GetVoicePitch());

		m_bInBarnacleMouth	= false;
		SetAbsVelocity( vec3_origin );
		SetMoveType( MOVETYPE_STEP );
		return true;
	}
	else if ( interactionType == g_interactionBarnacleVictimGrab )
	{
		if ( GetFlags() & FL_ONGROUND )
		{
			RemoveFlag( FL_ONGROUND );
		}
		
		if ( GetState() == NPC_STATE_SCRIPT )
		{
			 m_hCine->CancelScript();
			 ClearSchedule();
		}

		SetState( NPC_STATE_PRONE );
		PainSound();
		return true;
	}
	return false;
}

void CHL1NPCTalker::StartFollowing(	CBaseEntity *pLeader )
{
	if ( !HasSpawnFlags( SF_NPC_GAG ) )
	{
		Speak( TLK_USE );
		SetSpeechTarget( pLeader );
	}

	BaseClass::StartFollowing( pLeader );
}

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 ) )
		{
			Speak( TLK_UNUSE );
			SetSpeechTarget( GetFollowTarget() );
		}
	}

	BaseClass::StopFollowing();
}

void CHL1NPCTalker::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
{
	if ( info.GetDamage() >= 1.0 && !(info.GetDamageType() & DMG_SHOCK ) )
	{
		UTIL_BloodSpray( ptr->endpos, vecDir, BloodColor(), 4, FX_BLOODSPRAY_ALL );	
	}

	BaseClass::TraceAttack( info, vecDir, ptr );
}

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::PickRandomLookTarget( bool bExcludePlayers, float minTime, float maxTime )
{
	return random->RandomFloat( 5.0f, 10.0f );
}

void CHL1NPCTalker::IdleHeadTurn( const Vector &vTargetPos, float flDuration, float flImportance )
{
	if (!(CapabilitiesGet() & bits_CAP_TURN_HEAD))
		return;

	if ( flDuration == 0.0f )
		 flDuration = random->RandomFloat( 2.0, 4.0 );

	if ( vTargetPos == vec3_origin || m_NPCState == NPC_STATE_SCRIPT )
	{
		SetHeadDirection( vTargetPos, GetAnimTimeInterval() );
	}
	else
	{
		 AddLookTarget( vTargetPos, 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

	if ( m_NPCState != NPC_STATE_SCRIPT )
		 BaseClass::SetHeadDirection( vTargetPos, flInterval );
}

//-----------------------------------------------------------------------------
// 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;
}

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()

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -