diff options
Diffstat (limited to 'game/server/hl2/npc_alyx.cpp')
| -rw-r--r-- | game/server/hl2/npc_alyx.cpp | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/game/server/hl2/npc_alyx.cpp b/game/server/hl2/npc_alyx.cpp new file mode 100644 index 0000000..f9dc128 --- /dev/null +++ b/game/server/hl2/npc_alyx.cpp @@ -0,0 +1,272 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Alyx, the female sidekick and love interest that's taking the world by storm! +// +// Try the new Alyx Brite toothpaste! +// Alyx lederhosen! +// +// FIXME: need a better comment block +// +//=============================================================================// + +#include "cbase.h" +#include "npcevent.h" +#include "ai_basenpc.h" +#include "ai_hull.h" +#include "ai_basehumanoid.h" +#include "npc_alyx.h" +#include "ai_senses.h" +#include "soundent.h" +#include "props.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +LINK_ENTITY_TO_CLASS( npc_alyx, CNPC_Alyx ); + +BEGIN_DATADESC( CNPC_Alyx ) + + DEFINE_FIELD( m_hEmpTool, FIELD_EHANDLE ), + +END_DATADESC() + +int AE_ALYX_EMPTOOL_ATTACHMENT; +int AE_ALYX_EMPTOOL_SEQUENCE; + +//========================================================= +// Classify - indicates this NPC's place in the +// relationship table. +//========================================================= +Class_T CNPC_Alyx::Classify ( void ) +{ + return CLASS_PLAYER_ALLY_VITAL; +} + + +//========================================================= +// HandleAnimEvent - catches the NPC-specific messages +// that occur when tagged animation frames are played. +//========================================================= +void CNPC_Alyx::HandleAnimEvent( animevent_t *pEvent ) +{ + if (pEvent->event == AE_ALYX_EMPTOOL_ATTACHMENT) + { + if (!m_hEmpTool) + { + // Old savegame? + CreateEmpTool(); + if (!m_hEmpTool) + return; + } + + int iAttachment = LookupAttachment( pEvent->options ); + m_hEmpTool->SetParent(this, iAttachment); + m_hEmpTool->SetLocalOrigin( Vector( 0, 0, 0 ) ); + m_hEmpTool->SetLocalAngles( QAngle( 0, 0, 0 ) ); + + return; + } + else if (pEvent->event == AE_ALYX_EMPTOOL_SEQUENCE) + { + if (!m_hEmpTool) + return; + + CDynamicProp *pEmpTool = dynamic_cast<CDynamicProp *>(m_hEmpTool.Get()); + + if (!pEmpTool) + return; + + int iSequence = pEmpTool->LookupSequence( pEvent->options ); + if (iSequence != ACT_INVALID) + { + pEmpTool->PropSetSequence( iSequence ); + } + + return; + } + + switch( pEvent->event ) + { + case 1: + default: + BaseClass::HandleAnimEvent( pEvent ); + break; + } +} + +//========================================================= +// +//========================================================= +bool CNPC_Alyx::CreateBehaviors() +{ + return BaseClass::CreateBehaviors(); +} + + +//========================================================= +// Spawn +//========================================================= +void CNPC_Alyx::Spawn() +{ + BaseClass::Spawn(); + + // If Alyx has a parent, she's currently inside a pod. Prevent her from moving. + if ( GetMoveParent() ) + { + SetMoveType( MOVETYPE_NONE ); + CapabilitiesClear(); + + CapabilitiesAdd( bits_CAP_ANIMATEDFACE | bits_CAP_TURN_HEAD ); + CapabilitiesAdd( bits_CAP_FRIENDLY_DMG_IMMUNE ); + } + else + { + SetupAlyxWithoutParent(); + CreateEmpTool( ); + } + + AddEFlags( EFL_NO_DISSOLVE | EFL_NO_MEGAPHYSCANNON_RAGDOLL | EFL_NO_PHYSCANNON_INTERACTION ); + + m_iHealth = 80; + + NPCInit(); +} + +//========================================================= +// Precache - precaches all resources this NPC needs +//========================================================= +void CNPC_Alyx::Precache() +{ + BaseClass::Precache(); + PrecacheScriptSound( "npc_alyx.die" ); + PrecacheModel( STRING( GetModelName() ) ); + PrecacheModel( "models/alyx_emptool_prop.mdl" ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Alyx::SelectModel() +{ + // Alyx is allowed to use multiple models, because she appears in the pod. + // She defaults to her normal model. + const char *szModel = STRING( GetModelName() ); + if (!szModel || !*szModel) + { + SetModelName( AllocPooledString("models/alyx.mdl") ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Alyx::SetupAlyxWithoutParent( void ) +{ + SetSolid( SOLID_BBOX ); + AddSolidFlags( FSOLID_NOT_STANDABLE ); + SetMoveType( MOVETYPE_STEP ); + + CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_DOORS_GROUP | bits_CAP_TURN_HEAD | bits_CAP_DUCK | bits_CAP_SQUAD ); + CapabilitiesAdd( bits_CAP_USE_WEAPONS ); + CapabilitiesAdd( bits_CAP_ANIMATEDFACE ); + CapabilitiesAdd( bits_CAP_FRIENDLY_DMG_IMMUNE ); + CapabilitiesAdd( bits_CAP_AIM_GUN ); + CapabilitiesAdd( bits_CAP_MOVE_SHOOT ); + CapabilitiesAdd( bits_CAP_USE_SHOT_REGULATOR ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CNPC_Alyx::CreateEmpTool( void ) +{ + m_hEmpTool = (CBaseAnimating*)CreateEntityByName( "prop_dynamic" ); + if ( m_hEmpTool ) + { + m_hEmpTool->SetModel( "models/alyx_emptool_prop.mdl" ); + m_hEmpTool->SetName( AllocPooledString("Alyx_Emptool") ); + int iAttachment = LookupAttachment( "Emp_Holster" ); + m_hEmpTool->SetParent(this, iAttachment); + m_hEmpTool->SetOwnerEntity(this); + m_hEmpTool->SetSolid( SOLID_NONE ); + m_hEmpTool->SetLocalOrigin( Vector( 0, 0, 0 ) ); + m_hEmpTool->SetLocalAngles( QAngle( 0, 0, 0 ) ); + m_hEmpTool->Spawn(); + } +} + + + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CNPC_Alyx::PrescheduleThink( void ) +{ + BaseClass::PrescheduleThink(); + + // Figure out if Alyx has just been removed from her parent + if ( GetMoveType() == MOVETYPE_NONE && !GetMoveParent() ) + { + SetupAlyxWithoutParent(); + SetupVPhysicsHull(); + } + + if ( HasCondition( COND_TALKER_PLAYER_DEAD ) ) + { + SpeakIfAllowed( TLK_PLDEAD ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CNPC_Alyx::NPC_TranslateActivity( Activity activity ) +{ + activity = BaseClass::NPC_TranslateActivity( activity ); + if ( activity == ACT_IDLE && (m_NPCState == NPC_STATE_COMBAT || m_NPCState == NPC_STATE_ALERT) ) + { + if (gpGlobals->curtime - m_flLastAttackTime < 3 || gpGlobals->curtime - GetEnemyLastTimeSeen() < 8) + { + activity = ACT_IDLE_ANGRY; + } + } + + return activity; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- + +void CNPC_Alyx::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) +{ + BaseClass::TraceAttack( info, vecDir, ptr, pAccumulator ); + + // FIXME: hack until some way of removing decals after healing + m_fNoDamageDecal = true; +} + +//----------------------------------------------------------------------------- + +void CNPC_Alyx::DeathSound( const CTakeDamageInfo &info ) +{ + // Sentences don't play on dead NPCs + SentenceStop(); + + EmitSound( "npc_alyx.die" ); +} + +//========================================================= +// AI Schedules Specific to this NPC +//========================================================= + +AI_BEGIN_CUSTOM_NPC( npc_alyx, CNPC_Alyx ) + + DECLARE_ANIMEVENT( AE_ALYX_EMPTOOL_ATTACHMENT ) + DECLARE_ANIMEVENT( AE_ALYX_EMPTOOL_SEQUENCE ) + +AI_END_CUSTOM_NPC() + |