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

📄 hl1_npc_scientist.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//=========== (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_Route.h"
#include	"hl1_npc_scientist.h"
#include	"soundent.h"
#include	"game.h"
#include	"NPCEvent.h"
#include	"EntityList.h"
#include	"activitylist.h"
#include	"animation.h"
#include	"engine/IEngineSound.h"
#include	"ai_navigator.h"
#include	"AI_Behavior_Follow.h"
#include	"AI_Criteria.h"
#include	"doors.h"

#define SC_PLFEAR	"SC_PLFEAR"
#define SC_FEAR		"SC_FEAR"
#define SC_HEAL		"SC_HEAL"
#define SC_SCREAM	"SC_SCREAM"
#define SC_POK		"SC_POK"

ConVar	sk_scientist_health( "sk_scientist_health","20");
ConVar	sk_scientist_heal( "sk_scientist_heal","25");

#define		NUM_SCIENTIST_HEADS		4 // four heads available for scientist model
enum { HEAD_GLASSES = 0, HEAD_EINSTEIN = 1, HEAD_LUTHER = 2, HEAD_SLICK = 3 };


int ACT_EXCITED;

//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define		SCIENTIST_AE_HEAL		( 1 )
#define		SCIENTIST_AE_NEEDLEON	( 2 )
#define		SCIENTIST_AE_NEEDLEOFF	( 3 )

//=======================================================
// Scientist
//=======================================================

LINK_ENTITY_TO_CLASS( monster_scientist, CNPC_Scientist );

//IMPLEMENT_SERVERCLASS_ST( CNPC_Scientist, DT_NPC_Scientist )
//END_SEND_TABLE()


//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_Scientist )
	DEFINE_FIELD( CNPC_Scientist, m_flFearTime, FIELD_TIME ),
	DEFINE_FIELD( CNPC_Scientist, m_flHealTime, FIELD_TIME ),
	DEFINE_FIELD( CNPC_Scientist, m_flPainTime, FIELD_TIME ),
END_DATADESC()

//-----------------------------------------------------------------------------
// Purpose: 
//
//
//-----------------------------------------------------------------------------
void CNPC_Scientist::Precache( void )
{
	engine->PrecacheModel( "models/scientist.mdl" );

	enginesound->PrecacheSound("scientist/sci_pain1.wav");
	enginesound->PrecacheSound("scientist/sci_pain2.wav");
	enginesound->PrecacheSound("scientist/sci_pain3.wav");
	enginesound->PrecacheSound("scientist/sci_pain4.wav");
	enginesound->PrecacheSound("scientist/sci_pain5.wav");

	enginesound->PrecacheSound("scientist/c1a4_sci_rocket.wav");

	
	
	TalkInit();
	
	BaseClass::Precache();
}

void CNPC_Scientist::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_Scientist::TalkInit()
{
	
	BaseClass::TalkInit();

	// scientist will try to talk to friends in this order:

	m_szFriends[0] = "monster_scientist";
	m_szFriends[1] = "monster_sitting_scientist";
	m_szFriends[2] = "monster_barney";

	// get voice for head
	switch (m_nBody % 3)
	{
	default:
	case HEAD_GLASSES:	GetExpresser()->SetVoicePitch( 105 );	break;	//glasses
	case HEAD_EINSTEIN: GetExpresser()->SetVoicePitch( 100 );	break;	//einstein
	case HEAD_LUTHER:	GetExpresser()->SetVoicePitch( 95 );	break;	//luther
	case HEAD_SLICK:	GetExpresser()->SetVoicePitch( 100 );	break;//slick
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//
//
//-----------------------------------------------------------------------------
void CNPC_Scientist::Spawn( void )
{

	//Select the body first if it's going to be random cause we set his voice pitch in Precache.
	if ( m_nBody == -1 )
		 m_nBody = random->RandomInt( 0, NUM_SCIENTIST_HEADS-1 );// pick a head, any head
	

	SetRenderColor( 255, 255, 255, 255 );
	
	Precache();

	SetModel( "models/scientist.mdl" );

	SetHullType(HULL_HUMAN);
	SetHullSizeNormal();

	SetSolid( SOLID_BBOX );
	AddSolidFlags( FSOLID_NOT_STANDABLE );
	SetMoveType( MOVETYPE_STEP );
	m_bloodColor		= BLOOD_COLOR_RED;
	m_fEffects			= 0;
	m_iHealth			= sk_scientist_health.GetFloat();
	m_flFieldOfView		= VIEW_FIELD_WIDE;
	m_NPCState			= NPC_STATE_NONE;

	CapabilitiesClear();
	CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_OPEN_DOORS | bits_CAP_AUTO_DOORS | bits_CAP_USE );
	CapabilitiesAdd( bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE );

	// White hands
	m_nSkin = 0;

	
	// Luther is black, make his hands black
	if ( m_nBody == HEAD_LUTHER )
		 m_nSkin = 1;
	
	NPCInit();

	SetUse( FollowerUse );
}

//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
Class_T	CNPC_Scientist::Classify( void )
{
	return	CLASS_HUMAN_PASSIVE;
}

int CNPC_Scientist :: GetSoundInterests ( void )
{
	return	SOUND_WORLD	|
			SOUND_COMBAT	|
			SOUND_DANGER	|
			SOUND_PLAYER;
}

//=========================================================
// HandleAnimEvent - catches the monster-specific messages
// that occur when tagged animation frames are played.
//=========================================================
void CNPC_Scientist::HandleAnimEvent( animevent_t *pEvent )
{
	switch( pEvent->event )
	{		
	case SCIENTIST_AE_HEAL:		// Heal my target (if within range)
		Heal();
		break;
	case SCIENTIST_AE_NEEDLEON:
	{
		int oldBody = m_nBody;
		m_nBody = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 1;
	}
		break;
	case SCIENTIST_AE_NEEDLEOFF:
	{
		int oldBody = m_nBody;
		m_nBody = (oldBody % NUM_SCIENTIST_HEADS) + NUM_SCIENTIST_HEADS * 0;
	}
		break;

	default:
		BaseClass::HandleAnimEvent( pEvent );
		break;
	}
}

void CNPC_Scientist::DeclineFollowing( void )
{
	GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + 10 );
	//SetSpeechTarget( GetTarget() );

	Speak( SC_POK );
}

void CNPC_Scientist :: Scream( void )
{
	if ( IsOkToSpeak() )
	{
		GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + 10 );
		SetSpeechTarget( GetEnemy() );
		Speak( SC_SCREAM );
	}
}

Activity CNPC_Scientist::GetStoppedActivity( void )
{ 
	if ( GetEnemy() != NULL ) 
		return (Activity)ACT_EXCITED;
	
	return BaseClass::GetStoppedActivity();
}

float CNPC_Scientist::MaxYawSpeed( void )
{
	switch( GetActivity() )
	{
	case ACT_TURN_LEFT:
	case ACT_TURN_RIGHT:
		return 160;
		break;
	case ACT_RUN:
		return 160;
		break;
	default:
		return 60;
		break;
	}
}

void CNPC_Scientist :: StartTask( const Task_t *pTask )
{
	switch( pTask->iTask )
	{
	case TASK_SAY_HEAL:

		GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + 2 );
		SetSpeechTarget( GetTarget() );
		Speak( SC_HEAL );

		TaskComplete();
		break;
	 
	case TASK_SCREAM:
		Scream();
		TaskComplete();
		break;

	case TASK_RANDOM_SCREAM:
		if ( random->RandomFloat( 0, 1 ) < pTask->flTaskData )
			Scream();
		TaskComplete();
		break;

	case TASK_SAY_FEAR:
		if ( IsOkToSpeak() )
		{
			GetExpresser()->BlockSpeechUntil( gpGlobals->curtime + 2 );
			SetSpeechTarget( GetEnemy() );
			if ( GetEnemy() && GetEnemy()->IsPlayer() )
				Speak( SC_PLFEAR );
			else
				Speak( SC_FEAR );
		}
		TaskComplete();
		break;

	case TASK_HEAL:
		SetIdealActivity( ACT_MELEE_ATTACK1 );
		break;

	case TASK_RUN_PATH_SCARED:
		GetNavigator()->SetMovementActivity( ACT_RUN_SCARED );
		break;

	case TASK_MOVE_TO_TARGET_RANGE_SCARED:
		{
			if ( GetTarget() == NULL)
			{
				TaskFail(FAIL_NO_TARGET);
			}
			else if ( (GetTarget()->GetAbsOrigin() - GetAbsOrigin()).Length() < 1 )
			{
				TaskComplete();
			}
		}
		break;

	default:
		BaseClass::StartTask( pTask );
		break;
	}
}

void CNPC_Scientist :: RunTask( const Task_t *pTask )
{
	switch ( pTask->iTask )
	{
	case TASK_RUN_PATH_SCARED:
		if ( !IsMoving() )
			TaskComplete();
		if ( random->RandomInt(0,31) < 8 )
			Scream();
		break;

	case TASK_MOVE_TO_TARGET_RANGE_SCARED:
		{
			float distance;

			if ( GetTarget() == NULL )
			{
				TaskFail(FAIL_NO_TARGET);
			}
			else
			{
				distance = ( GetNavigator()->GetPath()->ActualGoalPosition() - GetAbsOrigin() ).Length2D();
				// Re-evaluate when you think your finished, or the target has moved too far
				if ( (distance < pTask->flTaskData) || (GetNavigator()->GetPath()->ActualGoalPosition() - GetTarget()->GetAbsOrigin()).Length() > pTask->flTaskData * 0.5 )
				{
					GetNavigator()->GetPath()->ResetGoalPosition(GetTarget()->GetAbsOrigin());
					distance = ( GetNavigator()->GetPath()->ActualGoalPosition() - GetAbsOrigin() ).Length2D();
//					GetNavigator()->GetPath()->Find();
					GetNavigator()->SetGoal( GOALTYPE_TARGETENT );
				}

				// Set the appropriate activity based on an overlapping range
				// overlap the range to prevent oscillation
				// BUGBUG: this is checking linear distance (ie. through walls) and not path distance or even visibility
				if ( distance < pTask->flTaskData )
				{
					TaskComplete();
					GetNavigator()->GetPath()->Clear();		// Stop moving
				}
				else
				{
					if ( distance < 190 && GetNavigator()->GetMovementActivity() != ACT_WALK_SCARED )
						GetNavigator()->SetMovementActivity( ACT_WALK_SCARED );
					else if ( distance >= 270 && GetNavigator()->GetMovementActivity() != ACT_RUN_SCARED )
						GetNavigator()->SetMovementActivity( ACT_RUN_SCARED );
				}
			}
		}
		break;

	case TASK_HEAL:
		if ( IsSequenceFinished() )
		{
			TaskComplete();
		}
		else
		{
			if ( TargetDistance() > 90 )
				TaskComplete();

			if ( GetTarget() )
				 GetMotor()->SetIdealYaw( UTIL_VecToYaw( GetTarget()->GetAbsOrigin() - GetAbsOrigin() ) );

			//GetMotor()->SetYawSpeed( m_YawSpeed );
		}
		break;
	default:
		BaseClass::RunTask( pTask );
		break;
	}
}

int CNPC_Scientist :: OnTakeDamage_Alive( const CTakeDamageInfo &inputInfo )
{

	if ( inputInfo.GetInflictor() && inputInfo.GetInflictor()->GetFlags() & FL_CLIENT )
	{
		Remember( bits_MEMORY_PROVOKED );
		StopFollowing();
	}

	// make sure friends talk about it if player hurts scientist...
	return BaseClass::OnTakeDamage_Alive( inputInfo );
}

void CNPC_Scientist::Event_Killed( const CTakeDamageInfo &info )
{
	SetUse( NULL );	
	BaseClass::Event_Killed( info );
}

bool CNPC_Scientist::CanHeal( void )
{ 
	CBaseEntity *pTarget = GetFollowTarget();

	if ( pTarget == NULL )
		 return false;

	if ( pTarget->IsPlayer() == false )
		 return false;

	if ( (m_flHealTime > gpGlobals->curtime) || (pTarget->m_iHealth > (pTarget->m_iMaxHealth * 0.5)) )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CNPC_Scientist::OnUpcomingDoor( AILocalMoveGoal_t *pMoveGoal, CBaseDoor *pDoor, float distClear, AIMoveResult_t *pResult )
{
	// If we can't get through the door, try and open it
	if ( BaseClass::OnUpcomingDoor( 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 );

⌨️ 快捷键说明

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