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

📄 npc_roller.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 2 页
字号:
//=========== (C) Copyright 1999 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:		This is the base version of the combine (not instanced only subclassed)
//
// $Workfile:     $
// $Date:         $
// $NoKeywords: $
//=============================================================================

#include "cbase.h"
#include "AI_Default.h"
#include "AI_Task.h"
#include "AI_Schedule.h"
#include "AI_Hull.h"
#include "AI_Hint.h"
#include "AI_Navigator.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "activitylist.h"
#include "AI_BaseNPC.h"
#include "AI_Node.h"
#include "ndebugoverlay.h"
#include "EntityList.h"
#include "NPC_Roller.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "physics_saverestore.h"


#define TIME_NEVER	1e9

#define ROLLER_UPRIGHT_SPEED	100
#define ROLLER_FORWARD_SPEED	-900.0
#define ROLLER_CHECK_DIST		24.0

#define ROLLER_MARCO_FREQUENCY	6

#define ROLLER_MARCOPOLO_DIST	300
#define ROLLER_NUM_FAILS		2

#define ROLLER_MAX_PUSH_MASS	50

#define ROLLER_BASH_FORCE		10000

#define ROLLER_DEBUG

static const char *pCodeSounds[] =
{
	"npc/roller/code1.wav",
	"npc/roller/code2.wav",
	"npc/roller/code3.wav",
	"npc/roller/code4.wav",
	"npc/roller/code5.wav",
	"npc/roller/code6.wav",
};


BEGIN_SIMPLE_DATADESC( CRollerController )

	DEFINE_FIELD( CRollerController, m_vecAngular, FIELD_VECTOR ),
	DEFINE_FIELD( CRollerController, m_vecLinear, FIELD_VECTOR ),
	DEFINE_FIELD( CRollerController, m_fIsStopped, FIELD_BOOLEAN ),

END_DATADESC()


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
IMotionEvent::simresult_e CRollerController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
{
	if( m_fIsStopped )
	{
		return SIM_NOTHING;
	}

	linear = m_vecLinear;
	angular = m_vecAngular;
	
	return IMotionEvent::SIM_LOCAL_ACCELERATION;
}


LINK_ENTITY_TO_CLASS( npc_roller, CNPC_Roller );
IMPLEMENT_CUSTOM_AI( npc_roller, CNPC_Roller );


//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CNPC_Roller )

	DEFINE_FIELD( CNPC_Roller, m_flTimeMarcoSound, FIELD_TIME ),
	DEFINE_FIELD( CNPC_Roller, m_flTimePoloSound, FIELD_TIME ),
	DEFINE_FIELD( CNPC_Roller, m_fHACKJustSpawned, FIELD_BOOLEAN ),
	DEFINE_FIELD( CNPC_Roller, m_vecUnstickDirection, FIELD_VECTOR ),
	DEFINE_FIELD( CNPC_Roller, m_flLastZPos, FIELD_FLOAT ),
	DEFINE_FIELD( CNPC_Roller, m_iFail, FIELD_INTEGER ),
	DEFINE_FIELD( CNPC_Roller, m_flForwardSpeed, FIELD_FLOAT ),
	DEFINE_ARRAY( CNPC_Roller, m_iAccessCode, FIELD_STRING,  ROLLER_CODE_DIGITS  ),
	DEFINE_FIELD( CNPC_Roller, m_iCodeProgress, FIELD_INTEGER ),
	DEFINE_EMBEDDED( CNPC_Roller, m_RollerController ),
	DEFINE_PHYSPTR( CNPC_Roller, m_pMotionController ),
	DEFINE_SOUNDPATCH( CNPC_Roller,  m_pRollSound ),

END_DATADESC()


//-----------------------------------------------------------------------------
// Purpose: Initialize the custom schedules
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CNPC_Roller::InitCustomSchedules(void) 
{
	INIT_CUSTOM_AI( CNPC_Roller );

	ADD_CUSTOM_SCHEDULE( CNPC_Roller,	SCHED_ROLLER_PATROL );
	ADD_CUSTOM_SCHEDULE( CNPC_Roller,	SCHED_ROLLER_WAIT_FOR_PHYSICS );
	ADD_CUSTOM_SCHEDULE( CNPC_Roller,	SCHED_ROLLER_UNSTICK );

	ADD_CUSTOM_CONDITION( CNPC_Roller,	COND_ROLLER_PHYSICS );

	ADD_CUSTOM_TASK( CNPC_Roller,		TASK_ROLLER_WAIT_FOR_PHYSICS );
	ADD_CUSTOM_TASK( CNPC_Roller,		TASK_ROLLER_FIND_PATROL_NODE );
	ADD_CUSTOM_TASK( CNPC_Roller,		TASK_ROLLER_UNSTICK );
	ADD_CUSTOM_TASK( CNPC_Roller,		TASK_ROLLER_ON );
	ADD_CUSTOM_TASK( CNPC_Roller,		TASK_ROLLER_OFF );
	ADD_CUSTOM_TASK( CNPC_Roller,		TASK_ROLLER_ISSUE_CODE );

	AI_LOAD_SCHEDULE( CNPC_Roller,	SCHED_ROLLER_PATROL );
	AI_LOAD_SCHEDULE( CNPC_Roller,	SCHED_ROLLER_WAIT_FOR_PHYSICS );
	AI_LOAD_SCHEDULE( CNPC_Roller,	SCHED_ROLLER_UNSTICK );

#if 0
	ADD_CUSTOM_ACTIVITY(CNPC_Roller,	ACT_MYCUSTOMACTIVITY);
#endif
}

//-----------------------------------------------------------------------------
// Purpose: cache all the pre's`
//
//
//-----------------------------------------------------------------------------
void CNPC_Roller::Precache( void )
{
	engine->PrecacheModel( "models/roller.mdl" );

	PRECACHE_SOUND_ARRAY( pCodeSounds );

	BaseClass::Precache();
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
//-----------------------------------------------------------------------------
void CNPC_Roller::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr )
{
	Vector vecSparkDir;

	vecSparkDir.x = random->RandomFloat( -0.5, 0.5 );
	vecSparkDir.y = random->RandomFloat( -0.5, 0.5 );
	vecSparkDir.z = random->RandomFloat( -0.5, 0.5 );
	
	g_pEffects->Ricochet( ptr->endpos, vecSparkDir );

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


//-----------------------------------------------------------------------------
// Purpose: This indicates which types of damage are considered to have physics
//			implications for this type of roller.
//
//-----------------------------------------------------------------------------
int CNPC_Roller::RollerPhysicsDamageMask( void )
{
	return ( DMG_PHYSGUN | DMG_BULLET | DMG_BLAST | DMG_SONIC );
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
//-----------------------------------------------------------------------------
void CNPC_Roller::Spawn( void )
{
	Precache();

	m_pRollSound = NULL;
	
	m_flForwardSpeed = ROLLER_FORWARD_SPEED;

	SetModel( "models/roller.mdl" );
	SetHullType( HULL_TINY_CENTERED );
	SetHullSizeNormal();

	m_bloodColor		= DONT_BLEED;
	m_iHealth			= 20;
	m_flFieldOfView		= 0.5;
	m_NPCState			= NPC_STATE_NONE;

	m_fHACKJustSpawned = true;

	m_RollerController.Off();

	m_flTimeMarcoSound = gpGlobals->curtime + ROLLER_MARCO_FREQUENCY * random->RandomFloat( 1, 3 );
	m_flTimePoloSound = TIME_NEVER;

	CapabilitiesClear();
	CapabilitiesAdd( bits_CAP_MOVE_GROUND );

	m_iFail = 0;

	// Create the object in the physics system
	IPhysicsObject *pPhysicsObject = VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
	m_pMotionController = physenv->CreateMotionController( &m_RollerController );
	m_pMotionController->AttachObject( pPhysicsObject );

	NPCInit();

	// Generate me an access code
	int i;
	int iLastDigit = -10;

	for( i = 0 ; i < ROLLER_CODE_DIGITS ; i++ )
	{
		// Generate a digit, and make sure it's not the same
		// or sequential to the previous digit.
		do
		{
			m_iAccessCode[ i ] = rand() % 6;
		} while( abs(m_iAccessCode[ i ] - iLastDigit) <= 1 );

		iLastDigit = m_iAccessCode[ i ];
	}

	// this suppresses boatloads of warnings in the movement code.
	// (code that assumes everyone is propelled by animation).
	m_flGroundSpeed = 20;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CNPC_Roller::~CNPC_Roller( void )
{
	physenv->DestroyMotionController( m_pMotionController );
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
bool CNPC_Roller::FInViewCone( CBaseEntity *pEntity )
{
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
int CNPC_Roller::OnTakeDamage_Alive( const CTakeDamageInfo &info )
{
	return VPhysicsTakeDamage( info );
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
Class_T	CNPC_Roller::Classify( void )
{
	return	CLASS_NONE;
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
bool CNPC_Roller::FValidateHintType(CAI_Hint *pHint)
{
	return(pHint->HintType() == HINT_ROLLER_PATROL_POINT);
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
Vector vecDelta( 64, 64, 64 );
void CNPC_Roller::Unstick( void )
{
	CBaseEntity		*pList[ 16 ];
	IPhysicsObject	*pPhysObj;
	int				i;
	Vector			vecDirToEnemy;
	Vector			vecDirToObject;

	m_flWaitFinished = gpGlobals->curtime;

	int count = UTIL_EntitiesInBox( pList, 16, GetAbsOrigin() - vecDelta, GetAbsOrigin() + vecDelta, 0 );

	m_vecUnstickDirection = vec3_origin;

	for( i = 0 ; i < count ; i++ )
	{
		pPhysObj = pList[ i ]->VPhysicsGetObject();

		if( !pPhysObj || pList[ i ]->m_iClassname == m_iClassname )
		{
			// Only consider physics objects. Exclude rollers.
			continue;
		}

		if( pPhysObj->GetMass() <= ROLLER_MAX_PUSH_MASS )
		{
			// Try to bash this physics object.
			trace_t tr;

			AI_TraceLine( GetAbsOrigin(), pList[ i ]->GetAbsOrigin(), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); 

			if( tr.fraction == 1.0 || tr.m_pEnt == pList[ i ] )
			{
				// Roll towards this item if the trace hits nothing, or
				// the trace hits the object.
				Vector vecBashDir;

				vecBashDir = pList[ i ]->GetAbsOrigin() - GetAbsOrigin();
				VectorNormalize( vecBashDir );
				vecBashDir.z = 0.0;

				//NDebugOverlay::Line( GetAbsOrigin(), pList[ i ]->GetAbsOrigin(), 0,255,0, true, 2 );

				m_vecUnstickDirection = vecBashDir * 80;

				return;
			}
		}
	}

	// No physics objects. Just pick a direction with some clearance and go there.

#define ROLLER_UNSTICK_DIST 80
	Vector vecDirections[ 4 ] = 
	{
		Vector(  0,	ROLLER_UNSTICK_DIST, 0 ),
		Vector(  ROLLER_UNSTICK_DIST,	0, 0 ),
		Vector(  0, -ROLLER_UNSTICK_DIST, 0 ),
		Vector( -ROLLER_UNSTICK_DIST,  0, 0 )
	};

	trace_t tr;

	for( i = 0 ; i < 4 ; i++ )
	{
		AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vecDirections[ i ], MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); 

		if( tr.fraction == 1.0 )
		{
			m_vecUnstickDirection = vecDirections[ i ];

			//NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin() + m_vecUnstickDirection, 255,255,0, true, 2 );

			// Roll in this direction for a couple of seconds.
			Msg( "unsticking!\n" );
			return;
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Keeps wobbling down by trying to keep the roller upright. 
//
//
// Output : 
//-----------------------------------------------------------------------------
void CNPC_Roller::RemainUpright( void )
{
	if( !m_RollerController.IsOn() )
	{
		// Don't bother with the math if the controller is off.
		return;
	}

	// We're going to examine the Z component of the Right vector 
	// to see how upright we are.
	Vector vecRight;

	AngleVectors( GetLocalAngles(), NULL, &vecRight, NULL );
	
	//Msg( "%f\n", vecRight.z );

	if( vecRight.z > 0.0001 )
	{
		Msg( "-torque\n" );
		m_RollerController.m_vecAngular.x = ROLLER_UPRIGHT_SPEED;
	}
	else if ( vecRight.z < -0.0001 )
	{
		Msg( "+torque\n" );
		m_RollerController.m_vecAngular.x = -ROLLER_UPRIGHT_SPEED;
	}
	else 
	{
		m_RollerController.m_vecAngular.x = 0;
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
void CNPC_Roller::PowerOnSound( void )
{
	EmitSound( "NPC_Roller.PowerOn" );
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
void CNPC_Roller::PowerOffSound( void )
{
	EmitSound( "NPC_Roller.PowerOff" );
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
void CNPC_Roller::PowerOn( void )
{
	if( !m_RollerController.IsOn() )
	{
		// If we're about to actually turn the controller on, make a sound.
		// Don't make the sound if the controller is already on.
		PowerOnSound();
	}

	m_RollerController.On();
}


//-----------------------------------------------------------------------------
// Purpose: 
//
//
// Output : 
//-----------------------------------------------------------------------------
void CNPC_Roller::PowerOff( void )
{
	if( m_RollerController.IsOn() )
	{
		// If we're about to actually turn the controller off, make a sound.
		// Don't make the sound if the controller is already off.
		PowerOffSound();
	}

	m_RollerController.Off();

⌨️ 快捷键说明

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