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

📄 hl1_npc_hgrunt.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 5 页
字号:
//=========== (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	"beam_shared.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	"AI_Squad.h"
#include	"AI_SquadSlot.h"
#include	"AI_Motor.h"
#include	"hl1_npc_hgrunt.h"
#include	"soundent.h"
#include	"game.h"
#include	"NPCEvent.h"
#include	"EntityList.h"
#include	"activitylist.h"
#include	"animation.h"
#include	"engine/IEngineSound.h"
#include	"ammodef.h"
#include	"basecombatweapon.h"
#include	"hl1_basegrenade.h"
#include	"customentity.h"
#include	"ai_interactions.h"
#include	"scripted.h"
#include	"hl1_basegrenade.h"
#include	"hl1_grenade_mp5.h"

ConVar	sk_hgrunt_health( "sk_hgrunt_health","0");
ConVar  sk_hgrunt_kick ( "sk_hgrunt_kick", "0" );
ConVar  sk_hgrunt_pellets ( "sk_hgrunt_pellets", "0" );
ConVar  sk_hgrunt_gspeed ( "sk_hgrunt_gspeed", "0" );

extern ConVar sk_plr_dmg_grenade;
extern ConVar sk_plr_dmg_mp5_grenade;

#define SF_GRUNT_LEADER	( 1 << 5  )

int g_fGruntQuestion;				// true if an idle grunt asked a question. Cleared when someone answers.
int g_iSquadIndex = 0;

#define HGRUNT_GUN_SPREAD 0.08716f

//=========================================================
// monster-specific DEFINE's
//=========================================================
#define	GRUNT_CLIP_SIZE					36 // how many bullets in a clip? - NOTE: 3 round burst sound, so keep as 3 * x!
#define GRUNT_VOL						0.35		// volume of grunt sounds
#define GRUNT_SNDLVL					SNDLVL_NORM	// soundlevel of grunt sentences
#define HGRUNT_LIMP_HEALTH				20
#define HGRUNT_DMG_HEADSHOT				( DMG_BULLET | DMG_CLUB )	// damage types that can kill a grunt with a single headshot.
#define HGRUNT_NUM_HEADS				2 // how many grunt heads are there? 
#define HGRUNT_MINIMUM_HEADSHOT_DAMAGE	15 // must do at least this much damage in one shot to head to score a headshot kill
#define	HGRUNT_SENTENCE_VOLUME			(float)0.35 // volume of grunt sentences

#define HGRUNT_9MMAR				( 1 << 0)
#define HGRUNT_HANDGRENADE			( 1 << 1)
#define HGRUNT_GRENADELAUNCHER		( 1 << 2)
#define HGRUNT_SHOTGUN				( 1 << 3)

#define HEAD_GROUP					1
#define HEAD_GRUNT					0
#define HEAD_COMMANDER				1
#define HEAD_SHOTGUN				2
#define HEAD_M203					3
#define GUN_GROUP					2
#define GUN_MP5						0
#define GUN_SHOTGUN					1
#define GUN_NONE					2

//=========================================================
// Monster's Anim Events Go Here
//=========================================================
#define		HGRUNT_AE_RELOAD		( 2 )
#define		HGRUNT_AE_KICK			( 3 )
#define		HGRUNT_AE_BURST1		( 4 )
#define		HGRUNT_AE_BURST2		( 5 ) 
#define		HGRUNT_AE_BURST3		( 6 ) 
#define		HGRUNT_AE_GREN_TOSS		( 7 )
#define		HGRUNT_AE_GREN_LAUNCH	( 8 )
#define		HGRUNT_AE_GREN_DROP		( 9 )
#define		HGRUNT_AE_CAUGHT_ENEMY	( 10) // grunt established sight with an enemy (player only) that had previously eluded the squad.
#define		HGRUNT_AE_DROP_GUN		( 11) // grunt (probably dead) is dropping his mp5.


const char *CNPC_HGrunt::pGruntSentences[] = 
{
	"HG_GREN", // grenade scared grunt
	"HG_ALERT", // sees player
	"HG_MONSTER", // sees monster
	"HG_COVER", // running to cover
	"HG_THROW", // about to throw grenade
	"HG_CHARGE",  // running out to get the enemy
	"HG_TAUNT", // say rude things
};

enum
{
	HGRUNT_SENT_NONE = -1,
	HGRUNT_SENT_GREN = 0,
	HGRUNT_SENT_ALERT,
	HGRUNT_SENT_MONSTER,
	HGRUNT_SENT_COVER,
	HGRUNT_SENT_THROW,
	HGRUNT_SENT_CHARGE,
	HGRUNT_SENT_TAUNT,
} HGRUNT_SENTENCE_TYPES;

LINK_ENTITY_TO_CLASS( monster_human_grunt, CNPC_HGrunt );

//=========================================================
// monster-specific schedule types
//=========================================================
enum
{
	SCHED_GRUNT_FAIL = LAST_SHARED_SCHEDULE,
	SCHED_GRUNT_COMBAT_FAIL,
	SCHED_GRUNT_VICTORY_DANCE,
	SCHED_GRUNT_ESTABLISH_LINE_OF_FIRE,
	SCHED_GRUNT_FOUND_ENEMY,
	SCHED_GRUNT_COMBAT_FACE,
	SCHED_GRUNT_SIGNAL_SUPPRESS,
	SCHED_GRUNT_SUPPRESS,
	SCHED_GRUNT_WAIT_IN_COVER,
	SCHED_GRUNT_TAKE_COVER,
	SCHED_GRUNT_GRENADE_COVER,
	SCHED_GRUNT_TOSS_GRENADE_COVER,
	SCHED_GRUNT_HIDE_RELOAD,
	SCHED_GRUNT_SWEEP,
	SCHED_GRUNT_RANGE_ATTACK1A,
	SCHED_GRUNT_RANGE_ATTACK1B,
	SCHED_GRUNT_RANGE_ATTACK2,
	SCHED_GRUNT_REPEL,
	SCHED_GRUNT_REPEL_ATTACK,
	SCHED_GRUNT_REPEL_LAND,
	SCHED_GRUNT_TAKE_COVER_FAILED,
	SCHED_GRUNT_RELOAD,
	SCHED_GRUNT_TAKE_COVER_FROM_ENEMY,
	SCHED_GRUNT_BARNACLE_HIT,
	SCHED_GRUNT_BARNACLE_PULL,
	SCHED_GRUNT_BARNACLE_CHOMP,
	SCHED_GRUNT_BARNACLE_CHEW,
};

//=========================================================
// monster-specific tasks
//=========================================================
enum 
{
	TASK_GRUNT_FACE_TOSS_DIR = LAST_SHARED_TASK + 1,
	TASK_GRUNT_SPEAK_SENTENCE,
	TASK_GRUNT_CHECK_FIRE,
};


//=========================================================
// monster-specific conditions
//=========================================================
enum
{
	COND_GRUNT_NOFIRE = LAST_SHARED_CONDITION + 1,
};

// -----------------------------------------------
//	> Squad slots
// -----------------------------------------------
enum SquadSlot_T
{	
	SQUAD_SLOT_GRENADE1 = LAST_SHARED_SQUADSLOT,
	SQUAD_SLOT_GRENADE2,
	SQUAD_SLOT_ENGAGE1,
	SQUAD_SLOT_ENGAGE2,
};


int ACT_GRUNT_LAUNCH_GRENADE;
int ACT_GRUNT_TOSS_GRENADE;
int ACT_GRUNT_MP5_STANDING;
int ACT_GRUNT_MP5_CROUCHING;
int ACT_GRUNT_SHOTGUN_STANDING;
int ACT_GRUNT_SHOTGUN_CROUCHING;

//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_HGrunt )
	DEFINE_FIELD( CNPC_HGrunt, m_flNextGrenadeCheck, FIELD_TIME ),
	DEFINE_FIELD( CNPC_HGrunt, m_flNextPainTime, FIELD_TIME ),
	DEFINE_FIELD( CNPC_HGrunt, m_flCheckAttackTime, FIELD_FLOAT ),
	DEFINE_FIELD( CNPC_HGrunt, m_vecTossVelocity, FIELD_VECTOR ),
	DEFINE_FIELD( CNPC_HGrunt, m_iLastGrenadeCondition, FIELD_INTEGER ),
	DEFINE_FIELD( CNPC_HGrunt, m_fStanding, FIELD_BOOLEAN ),
	DEFINE_FIELD( CNPC_HGrunt, m_fFirstEncounter, FIELD_BOOLEAN ),
	DEFINE_FIELD( CNPC_HGrunt, m_iClipSize, FIELD_INTEGER ),
	DEFINE_FIELD( CNPC_HGrunt, m_voicePitch, FIELD_INTEGER ),
	DEFINE_FIELD( CNPC_HGrunt, m_iSentence, FIELD_INTEGER ),
	DEFINE_KEYFIELD( CNPC_HGrunt, m_iWeapons, FIELD_INTEGER, "weapons" ),
	DEFINE_KEYFIELD( CNPC_HGrunt, m_SquadName, FIELD_STRING, "netname" ),
END_DATADESC()


//=========================================================
// Spawn
//=========================================================
void CNPC_HGrunt::Spawn()
{
	Precache( );

	SetModel( "models/hgrunt.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_hgrunt_health.GetFloat();
	m_flFieldOfView		= 0.2;// indicates the width of this monster's forward view cone ( as a dotproduct result )
	m_NPCState			= NPC_STATE_NONE;
	m_flNextGrenadeCheck = gpGlobals->curtime + 1;
	m_flNextPainTime	= gpGlobals->curtime;
	m_iSentence			= HGRUNT_SENT_NONE;

	CapabilitiesClear();
	CapabilitiesAdd ( bits_CAP_SQUAD | bits_CAP_TURN_HEAD | bits_CAP_DOORS_GROUP | bits_CAP_MOVE_GROUND );

	CapabilitiesAdd(bits_CAP_INNATE_RANGE_ATTACK1 );

	// Innate range attack for grenade
	CapabilitiesAdd(bits_CAP_INNATE_RANGE_ATTACK2 );
	// Innate range attack for kicking
	CapabilitiesAdd(bits_CAP_INNATE_MELEE_ATTACK1 );
			
	m_fFirstEncounter	= true;// this is true when the grunt spawns, because he hasn't encountered an enemy yet.

	m_HackedGunPos = Vector ( 0, 0, 55 );

	if ( m_iWeapons == 0)
	{
		// initialize to original values
		m_iWeapons = HGRUNT_9MMAR | HGRUNT_HANDGRENADE;
		// pev->weapons = HGRUNT_SHOTGUN;
		// pev->weapons = HGRUNT_9MMAR | HGRUNT_GRENADELAUNCHER;
	}

	if (FBitSet( m_iWeapons, HGRUNT_SHOTGUN ))
	{
		SetBodygroup( GUN_GROUP, GUN_SHOTGUN );
		m_iClipSize		= 8;
	}
	else
	{
		m_iClipSize		= GRUNT_CLIP_SIZE;
	}
	m_cAmmoLoaded		= m_iClipSize;

	if ( random->RandomInt( 0, 99 ) < 80)
		m_nSkin = 0;	// light skin
	else
		m_nSkin = 1;	// dark skin

	if (FBitSet( m_iWeapons, HGRUNT_SHOTGUN ))
	{
		SetBodygroup( HEAD_GROUP, HEAD_SHOTGUN);
	}
	else if (FBitSet( m_iWeapons, HGRUNT_GRENADELAUNCHER ))
	{
		SetBodygroup( HEAD_GROUP, HEAD_M203 );
		m_nSkin = 1; // alway dark skin
	}

	m_flTalkWaitTime = 0;


	//HACK
	g_iSquadIndex = 0;

	BaseClass::Spawn();

	NPCInit();
}

int CNPC_HGrunt::IRelationPriority( CBaseEntity *pTarget )
{
	//I hate alien grunts more than anything.
	if ( pTarget->Classify() == CLASS_ALIEN_MILITARY )
	{
		if ( FClassnameIs( pTarget, "monster_alien_grunt" ) )
		{
			 return ( BaseClass::IRelationPriority ( pTarget ) + 1 );
		}
	}

	return BaseClass::IRelationPriority( pTarget );
}

//=========================================================
// Precache - precaches all resources this monster needs
//=========================================================
void CNPC_HGrunt::Precache()
{
	m_iAmmoType = GetAmmoDef()->Index("9mmRound");

	engine->PrecacheModel("models/hgrunt.mdl");

	enginesound->PrecacheSound( "hgrunt/gr_mgun1.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_mgun2.wav" );
	
	enginesound->PrecacheSound( "hgrunt/gr_die1.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_die2.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_die3.wav" );

	enginesound->PrecacheSound( "hgrunt/gr_pain1.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_pain2.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_pain3.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_pain4.wav" );
	enginesound->PrecacheSound( "hgrunt/gr_pain5.wav" );

	enginesound->PrecacheSound( "hgrunt/gr_reload1.wav" );

	enginesound->PrecacheSound( "weapons/glauncher.wav" );

	enginesound->PrecacheSound( "weapons/sbarrel1.wav" );

	enginesound->PrecacheSound("zombie/claw_miss2.wav");// because we use the basemonster SWIPE animation event

	// get voice pitch
	if ( random->RandomInt(0,1))
		m_voicePitch = 109 + random->RandomInt(0,7);
	else
		m_voicePitch = 100;

	m_iBrassShell = engine->PrecacheModel ("models/shell.mdl");// brass shell
	m_iShotgunShell = engine->PrecacheModel ("models/shotgunshell.mdl");

	BaseClass::Precache();

	UTIL_PrecacheOther( "grenade_hand" );
	UTIL_PrecacheOther( "grenade_mp5" );
}	

//=========================================================
// someone else is talking - don't speak
//=========================================================
bool CNPC_HGrunt::FOkToSpeak( void )
{
// if someone else is talking, don't speak
	if ( gpGlobals->curtime <= m_flTalkWaitTime )
		 return FALSE;

	if ( m_spawnflags & SF_NPC_GAG )
	{
		if ( m_NPCState != NPC_STATE_COMBAT )
		{
			// no talking outside of combat if gagged.
			return FALSE;
		}
	}

	return TRUE;
}


//=========================================================
// Speak Sentence - say your cued up sentence.
//
// Some grunt sentences (take cover and charge) rely on actually
// being able to execute the intended action. It's really lame
// when a grunt says 'COVER ME' and then doesn't move. The problem
// is that the sentences were played when the decision to TRY
// to move to cover was made. Now the sentence is played after 
// we know for sure that there is a valid path. The schedule
// may still fail but in most cases, well after the grunt has 
// started moving.
//=========================================================
void CNPC_HGrunt::SpeakSentence( void )
{
	if ( m_iSentence == HGRUNT_SENT_NONE )
	{
		// no sentence cued up.
		return; 
	}

	if ( FOkToSpeak() )
	{
		SENTENCEG_PlayRndSz( edict(), pGruntSentences[ m_iSentence ], HGRUNT_SENTENCE_VOLUME, GRUNT_SNDLVL, 0, m_voicePitch);
		JustSpoke();
	}
}

//=========================================================
//=========================================================
void CNPC_HGrunt::JustSpoke( void )
{
	m_flTalkWaitTime = gpGlobals->curtime + random->RandomFloat( 1.5f, 2.0f );
	m_iSentence = HGRUNT_SENT_NONE;
}

//=========================================================
// PrescheduleThink - this function runs after conditions
// are collected and before scheduling code is run.
//=========================================================
void CNPC_HGrunt::PrescheduleThink ( void )
{
	BaseClass::PrescheduleThink();
	
	if ( m_pSquad && GetEnemy() != NULL )
	{
		if ( m_pSquad->GetLeader() == NULL )
			 return;

		CNPC_HGrunt *pSquadLeader = (CNPC_HGrunt*)m_pSquad->GetLeader()->MyNPCPointer();

		if ( pSquadLeader == NULL )
			 return; //Paranoid, so making sure it's ok.		
		
		if ( HasCondition ( COND_SEE_ENEMY ) )
		{
			// update the squad's last enemy sighting time.
			pSquadLeader->m_flLastEnemySightTime = gpGlobals->curtime;
		}
		else
		{
			if ( gpGlobals->curtime - pSquadLeader->m_flLastEnemySightTime > 5 )
			{
				// been a while since we've seen the enemy
				pSquadLeader->GetEnemies()->MarkAsEluded( GetEnemy() );
			}
		}
	}
}

Class_T	CNPC_HGrunt::Classify ( void )
{
	return CLASS_HUMAN_MILITARY;
}

//=========================================================
//
// SquadRecruit(), get some monsters of my classification and
// link them as a group.  returns the group size
//
//=========================================================
int CNPC_HGrunt::SquadRecruit( int searchRadius, int maxMembers )
{

⌨️ 快捷键说明

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