📄 hl1_npc_tentacle.cpp
字号:
//=========== (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 "AI_Senses.h"
#include "AI_Motor.h"
#include "soundent.h"
#include "game.h"
#include "NPCEvent.h"
#include "EntityList.h"
#include "activitylist.h"
#include "animation.h"
#include "basecombatweapon.h"
#include "IEffects.h"
#include "vstdlib/random.h"
#include "engine/IEngineSound.h"
#include "ammodef.h"
#include "hl1_ai_basenpc.h"
#include "studio.h" //hitbox parsing
#include "collisionutils.h" //ComputeSeparatingPlane
#include "physics_bone_follower.h" //For BoneFollowerManager
#define ACT_T_IDLE 1010
Activity ACT_1010;
Activity ACT_1011;
Activity ACT_1012;
Activity ACT_1013;
#define ACT_T_TAP 1020
Activity ACT_1020;
Activity ACT_1021;
Activity ACT_1022;
Activity ACT_1023;
#define ACT_T_STRIKE 1030
Activity ACT_1030;
Activity ACT_1031;
Activity ACT_1032;
Activity ACT_1033;
#define ACT_T_REARIDLE 1040
Activity ACT_1040;
Activity ACT_1041;
Activity ACT_1042;
Activity ACT_1043;
Activity ACT_1044;
class CNPC_Tentacle : public CHL1BaseNPC
{
DECLARE_CLASS( CNPC_Tentacle, CHL1BaseNPC );
public:
CNPC_Tentacle();
void Spawn( );
void Precache( );
bool KeyValue( const char *szKeyName, const char *szValue );
int Level( float dz );
int MyLevel( void );
float MyHeight( void );
// Don't allow the tentacle to go across transitions!!!
virtual int ObjectCaps( void ) { return CAI_BaseNPC::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
void Start ( void );
void Cycle ( void );
void HitTouch( CBaseEntity *pOther );
void SetObjectCollisionBox( void )
{
SetAbsMins( GetAbsOrigin() + Vector(-400, -400, 0) );
SetAbsMaxs( GetAbsOrigin() + Vector(400, 400, 850) );
}
void HandleAnimEvent( animevent_t *pEvent );
float HearingSensitivity( void ) { return 2.0; };
virtual int OnTakeDamage( const CTakeDamageInfo &info );
bool CreateVPhysics( void );
void UpdateOnRemove( void );
float m_flInitialYaw;
int m_iGoalAnim;
int m_iLevel;
int m_iDir;
float m_flFramerateAdj;
float m_flSoundYaw;
int m_iSoundLevel;
float m_flSoundTime;
float m_flSoundRadius;
int m_iHitDmg;
float m_flHitTime;
float m_flTapRadius;
float m_flNextSong;
static int g_fFlySound;
static int g_fSquirmSound;
float m_flMaxYaw;
int m_iTapSound;
Vector m_vecPrevSound;
float m_flPrevSoundTime;
static const char *pHitSilo[];
static const char *pHitDirt[];
static const char *pHitWater[];
float MaxYawSpeed( void ) { return 18.0f; }
bool HeardAnything( void );
Class_T Classify ( void );
CBoneFollowerManager m_BoneFollowerManager;
DECLARE_DATADESC();
DEFINE_CUSTOM_AI;
};
// Crane bones that have physics followers
static const char *pTentacleFollowerBoneNames[] =
{
"Bone08",
"Bone09"
};
int CNPC_Tentacle :: g_fFlySound;
int CNPC_Tentacle :: g_fSquirmSound;
LINK_ENTITY_TO_CLASS( monster_tentacle, CNPC_Tentacle );
// stike sounds
#define TE_NONE -1
#define TE_SILO 0
#define TE_DIRT 1
#define TE_WATER 2
const char *CNPC_Tentacle::pHitSilo[] =
{
"tentacle/te_strike1.wav",
"tentacle/te_strike2.wav",
};
const char *CNPC_Tentacle::pHitDirt[] =
{
"player/pl_dirt1.wav",
"player/pl_dirt2.wav",
"player/pl_dirt3.wav",
"player/pl_dirt4.wav",
};
const char *CNPC_Tentacle::pHitWater[] =
{
"player/pl_slosh1.wav",
"player/pl_slosh2.wav",
"player/pl_slosh3.wav",
"player/pl_slosh4.wav",
};
// animation sequence aliases
typedef enum
{
TENTACLE_ANIM_Pit_Idle,
TENTACLE_ANIM_rise_to_Temp1,
TENTACLE_ANIM_Temp1_to_Floor,
TENTACLE_ANIM_Floor_Idle,
TENTACLE_ANIM_Floor_Fidget_Pissed,
TENTACLE_ANIM_Floor_Fidget_SmallRise,
TENTACLE_ANIM_Floor_Fidget_Wave,
TENTACLE_ANIM_Floor_Strike,
TENTACLE_ANIM_Floor_Tap,
TENTACLE_ANIM_Floor_Rotate,
TENTACLE_ANIM_Floor_Rear,
TENTACLE_ANIM_Floor_Rear_Idle,
TENTACLE_ANIM_Floor_to_Lev1,
TENTACLE_ANIM_Lev1_Idle,
TENTACLE_ANIM_Lev1_Fidget_Claw,
TENTACLE_ANIM_Lev1_Fidget_Shake,
TENTACLE_ANIM_Lev1_Fidget_Snap,
TENTACLE_ANIM_Lev1_Strike,
TENTACLE_ANIM_Lev1_Tap,
TENTACLE_ANIM_Lev1_Rotate,
TENTACLE_ANIM_Lev1_Rear,
TENTACLE_ANIM_Lev1_Rear_Idle,
TENTACLE_ANIM_Lev1_to_Lev2,
TENTACLE_ANIM_Lev2_Idle,
TENTACLE_ANIM_Lev2_Fidget_Shake,
TENTACLE_ANIM_Lev2_Fidget_Swing,
TENTACLE_ANIM_Lev2_Fidget_Tut,
TENTACLE_ANIM_Lev2_Strike,
TENTACLE_ANIM_Lev2_Tap,
TENTACLE_ANIM_Lev2_Rotate,
TENTACLE_ANIM_Lev2_Rear,
TENTACLE_ANIM_Lev2_Rear_Idle,
TENTACLE_ANIM_Lev2_to_Lev3,
TENTACLE_ANIM_Lev3_Idle,
TENTACLE_ANIM_Lev3_Fidget_Shake,
TENTACLE_ANIM_Lev3_Fidget_Side,
TENTACLE_ANIM_Lev3_Fidget_Swipe,
TENTACLE_ANIM_Lev3_Strike,
TENTACLE_ANIM_Lev3_Tap,
TENTACLE_ANIM_Lev3_Rotate,
TENTACLE_ANIM_Lev3_Rear,
TENTACLE_ANIM_Lev3_Rear_Idle,
TENTACLE_ANIM_Lev1_Door_reach,
TENTACLE_ANIM_Lev3_to_Engine,
TENTACLE_ANIM_Engine_Idle,
TENTACLE_ANIM_Engine_Sway,
TENTACLE_ANIM_Engine_Swat,
TENTACLE_ANIM_Engine_Bob,
TENTACLE_ANIM_Engine_Death1,
TENTACLE_ANIM_Engine_Death2,
TENTACLE_ANIM_Engine_Death3,
TENTACLE_ANIM_none
} TENTACLE_ANIM;
BEGIN_DATADESC( CNPC_Tentacle )
DEFINE_FIELD( CNPC_Tentacle, m_flInitialYaw, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Tentacle, m_iGoalAnim, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_Tentacle, m_iLevel, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_Tentacle, m_iDir, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_Tentacle, m_flFramerateAdj, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Tentacle, m_flSoundYaw, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Tentacle, m_iSoundLevel, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_Tentacle, m_flSoundTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Tentacle, m_flSoundRadius, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Tentacle, m_iHitDmg, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_Tentacle, m_flHitTime, FIELD_TIME ),
DEFINE_FIELD( CNPC_Tentacle, m_flTapRadius, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Tentacle, m_flNextSong, FIELD_TIME ),
DEFINE_FIELD( CNPC_Tentacle, m_iTapSound, FIELD_INTEGER ),
DEFINE_FIELD( CNPC_Tentacle, m_flMaxYaw, FIELD_FLOAT ),
DEFINE_FIELD( CNPC_Tentacle, m_vecPrevSound, FIELD_POSITION_VECTOR ),
DEFINE_FIELD( CNPC_Tentacle, m_flPrevSoundTime, FIELD_TIME ),
DEFINE_THINKFUNC( CNPC_Tentacle, Start ),
DEFINE_THINKFUNC( CNPC_Tentacle, Cycle ),
DEFINE_ENTITYFUNC( CNPC_Tentacle, HitTouch ),
END_DATADESC()
Class_T CNPC_Tentacle::Classify ( void )
{
return CLASS_ALIEN_MONSTER;
}
CNPC_Tentacle::CNPC_Tentacle()
{
m_flMaxYaw = 65;
m_iTapSound = 0;
}
bool CNPC_Tentacle::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "sweeparc") )
{
m_flMaxYaw = atof( szValue ) / 2.0;
return true;
}
else if (FStrEq( szKeyName, "sound"))
{
m_iTapSound = atoi( szValue );
return true;
}
else
return BaseClass::KeyValue( szKeyName, szValue );
return false;
}
//
// Tentacle Spawn
//
void CNPC_Tentacle::Spawn( )
{
Precache( );
SetSolid( SOLID_BBOX );
//Necessary for TestCollision to be called for hitbox ray hits
AddSolidFlags( FSOLID_CUSTOMRAYTEST );
SetMoveType( MOVETYPE_FLY );
m_fEffects = 0;
m_iHealth = 75;
SetSequence( 0 );
SetModel( "models/tentacle2.mdl" );
UTIL_SetSize( this, Vector( -32, -32, 0 ), Vector( 32, 32, 64 ) );
m_takedamage = DAMAGE_AIM;
AddFlag( FL_NPC );
m_bloodColor = BLOOD_COLOR_GREEN;
ResetSequenceInfo( );
m_iDir = 1;
SetThink( Start );
SetNextThink( gpGlobals->curtime + 0.2 );
SetTouch( HitTouch );
m_flInitialYaw = GetAbsAngles().y;
GetMotor()->SetIdealYawAndUpdate( m_flInitialYaw );
g_fFlySound = FALSE;
g_fSquirmSound = FALSE;
m_iHitDmg = 200;
if (m_flMaxYaw <= 0)
m_flMaxYaw = 65;
m_NPCState = NPC_STATE_IDLE;
UTIL_SetOrigin( this, GetAbsOrigin() );
CreateVPhysics();
}
void CNPC_Tentacle::UpdateOnRemove( void )
{
m_BoneFollowerManager.DestroyBoneFollowers();
BaseClass::UpdateOnRemove();
}
void CNPC_Tentacle::Precache( )
{
engine->PrecacheModel("models/tentacle2.mdl");
enginesound->PrecacheSound("ambience/flies.wav");
enginesound->PrecacheSound("ambience/squirm2.wav");
enginesound->PrecacheSound("tentacle/te_alert1.wav");
enginesound->PrecacheSound("tentacle/te_alert2.wav");
enginesound->PrecacheSound("tentacle/te_flies1.wav");
enginesound->PrecacheSound("tentacle/te_move1.wav");
enginesound->PrecacheSound("tentacle/te_move2.wav");
enginesound->PrecacheSound("tentacle/te_roar1.wav");
enginesound->PrecacheSound("tentacle/te_roar2.wav");
enginesound->PrecacheSound("tentacle/te_search1.wav");
enginesound->PrecacheSound("tentacle/te_search2.wav");
enginesound->PrecacheSound("tentacle/te_sing1.wav");
enginesound->PrecacheSound("tentacle/te_sing2.wav");
enginesound->PrecacheSound("tentacle/te_squirm2.wav");
enginesound->PrecacheSound("tentacle/te_strike1.wav");
enginesound->PrecacheSound("tentacle/te_strike2.wav");
enginesound->PrecacheSound("tentacle/te_swing1.wav");
enginesound->PrecacheSound("tentacle/te_swing2.wav");
PRECACHE_SOUND_ARRAY( pHitSilo );
PRECACHE_SOUND_ARRAY( pHitDirt );
PRECACHE_SOUND_ARRAY( pHitWater );
BaseClass::Precache();
}
int CNPC_Tentacle::Level( float dz )
{
if (dz < 216)
return 0;
if (dz < 408)
return 1;
if (dz < 600)
return 2;
return 3;
}
float CNPC_Tentacle::MyHeight( )
{
switch ( MyLevel( ) )
{
case 1:
return 256;
case 2:
return 448;
case 3:
return 640;
}
return 0;
}
int CNPC_Tentacle::MyLevel( )
{
switch( GetSequence() )
{
case TENTACLE_ANIM_Pit_Idle:
return -1;
case TENTACLE_ANIM_rise_to_Temp1:
case TENTACLE_ANIM_Temp1_to_Floor:
case TENTACLE_ANIM_Floor_to_Lev1:
return 0;
case TENTACLE_ANIM_Floor_Idle:
case TENTACLE_ANIM_Floor_Fidget_Pissed:
case TENTACLE_ANIM_Floor_Fidget_SmallRise:
case TENTACLE_ANIM_Floor_Fidget_Wave:
case TENTACLE_ANIM_Floor_Strike:
case TENTACLE_ANIM_Floor_Tap:
case TENTACLE_ANIM_Floor_Rotate:
case TENTACLE_ANIM_Floor_Rear:
case TENTACLE_ANIM_Floor_Rear_Idle:
return 0;
case TENTACLE_ANIM_Lev1_Idle:
case TENTACLE_ANIM_Lev1_Fidget_Claw:
case TENTACLE_ANIM_Lev1_Fidget_Shake:
case TENTACLE_ANIM_Lev1_Fidget_Snap:
case TENTACLE_ANIM_Lev1_Strike:
case TENTACLE_ANIM_Lev1_Tap:
case TENTACLE_ANIM_Lev1_Rotate:
case TENTACLE_ANIM_Lev1_Rear:
case TENTACLE_ANIM_Lev1_Rear_Idle:
return 1;
case TENTACLE_ANIM_Lev1_to_Lev2:
return 1;
case TENTACLE_ANIM_Lev2_Idle:
case TENTACLE_ANIM_Lev2_Fidget_Shake:
case TENTACLE_ANIM_Lev2_Fidget_Swing:
case TENTACLE_ANIM_Lev2_Fidget_Tut:
case TENTACLE_ANIM_Lev2_Strike:
case TENTACLE_ANIM_Lev2_Tap:
case TENTACLE_ANIM_Lev2_Rotate:
case TENTACLE_ANIM_Lev2_Rear:
case TENTACLE_ANIM_Lev2_Rear_Idle:
return 2;
case TENTACLE_ANIM_Lev2_to_Lev3:
return 2;
case TENTACLE_ANIM_Lev3_Idle:
case TENTACLE_ANIM_Lev3_Fidget_Shake:
case TENTACLE_ANIM_Lev3_Fidget_Side:
case TENTACLE_ANIM_Lev3_Fidget_Swipe:
case TENTACLE_ANIM_Lev3_Strike:
case TENTACLE_ANIM_Lev3_Tap:
case TENTACLE_ANIM_Lev3_Rotate:
case TENTACLE_ANIM_Lev3_Rear:
case TENTACLE_ANIM_Lev3_Rear_Idle:
return 3;
case TENTACLE_ANIM_Lev1_Door_reach:
return -1;
}
return -1;
}
void CNPC_Tentacle::Start( void )
{
SetThink( Cycle );
CPASAttenuationFilter filter( this );
if ( !g_fFlySound )
{
enginesound->EmitSound( filter, entindex(), CHAN_BODY, "ambience/flies.wav", 1, ATTN_NORM );
g_fFlySound = TRUE;
}
else if ( !g_fSquirmSound )
{
enginesound->EmitSound( filter, entindex(), CHAN_BODY, "ambience/squirm2.wav", 1, ATTN_NORM );
g_fSquirmSound = TRUE;
}
SetNextThink( gpGlobals->curtime + 0.1 );
}
bool CNPC_Tentacle::HeardAnything( void )
{
if ( HasCondition( COND_HEAR_DANGER ) ||
HasCondition( COND_HEAR_THUMPER ) ||
HasCondition( COND_HEAR_BUGBAIT ) ||
HasCondition( COND_HEAR_COMBAT ) ||
HasCondition( COND_HEAR_WORLD ) ||
HasCondition( COND_HEAR_PLAYER ) ||
HasCondition( COND_HEAR_BULLET_IMPACT ) ||
HasCondition( COND_HEAR_PHYSICS_DANGER ) )
return true;
return false;
}
void CNPC_Tentacle::Cycle( void )
{
// ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState );
SetNextThink( gpGlobals->curtime + 0.1 );
// ALERT( at_console, "%s %d %d %d %f %f\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim, m_iDir, pev->framerate, pev->health );
if ( m_NPCState == NPC_STATE_SCRIPT || m_IdealNPCState == NPC_STATE_SCRIPT)
{
SetAbsAngles( QAngle( GetAbsAngles().x, m_flInitialYaw, GetAbsAngles().z ) );
GetMotor()->SetIdealYaw( m_flInitialYaw );
RemoveIgnoredConditions();
NPCThink( );
m_iGoalAnim = TENTACLE_ANIM_Pit_Idle;
return;
}
StudioFrameAdvance();
DispatchAnimEvents( this );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -