📄 npc_roller.cpp
字号:
//=========== (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 + -