diff --git a/sp/src/game/client/c_baseanimating.cpp b/sp/src/game/client/c_baseanimating.cpp index c5b54eef..0dd620d7 100644 --- a/sp/src/game/client/c_baseanimating.cpp +++ b/sp/src/game/client/c_baseanimating.cpp @@ -1990,6 +1990,10 @@ CollideType_t C_BaseAnimating::GetCollideType( void ) return BaseClass::GetCollideType(); } +#ifdef MAPBASE +ConVar ai_death_pose_enabled( "ai_death_pose_enabled", "1", FCVAR_NONE, "Toggles the death pose fix code, which cancels sequence transitions while a NPC is ragdolling." ); +#endif + //----------------------------------------------------------------------------- // Purpose: if the active sequence changes, keep track of the previous ones and decay them based on their decay rate //----------------------------------------------------------------------------- @@ -2006,6 +2010,14 @@ void C_BaseAnimating::MaintainSequenceTransitions( IBoneSetup &boneSetup, float return; } +#ifdef MAPBASE + if ( IsAboutToRagdoll() && ai_death_pose_enabled.GetBool() ) + { + m_nPrevNewSequenceParity = m_nNewSequenceParity; + return; + } +#endif + m_SequenceTransitioner.CheckForSequenceChange( boneSetup.GetStudioHdr(), GetSequence(), @@ -2785,14 +2797,29 @@ void C_BaseAnimating::CalculateIKLocks( float currentTime ) // debugoverlay->AddBoxOverlay( origin, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 0 ); - float d = (pTarget->est.pos - origin).Length(); + Vector vecDelta = (origin - pTarget->est.pos); + float d = vecDelta.Length(); if ( d >= flDist) continue; flDist = d; - pTarget->SetPos( origin ); - pTarget->SetAngles( angles ); +#ifdef MAPBASE + // For blending purposes, IK attachments should obey weight + if ( pTarget->est.flWeight < 1.0f ) + { + Quaternion qTarget; + AngleQuaternion( angles, qTarget ); + + QuaternionSlerp( pTarget->est.q, qTarget, pTarget->est.flWeight, pTarget->est.q ); + pTarget->SetPos( pTarget->est.pos + (vecDelta * pTarget->est.flWeight) ); + } + else +#endif + { + pTarget->SetPos( origin ); + pTarget->SetAngles( angles ); + } // debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 ); } diff --git a/sp/src/game/server/BaseAnimatingOverlay.cpp b/sp/src/game/server/BaseAnimatingOverlay.cpp index d04ff89d..06bf690e 100644 --- a/sp/src/game/server/BaseAnimatingOverlay.cpp +++ b/sp/src/game/server/BaseAnimatingOverlay.cpp @@ -1095,9 +1095,9 @@ void CBaseAnimatingOverlay::SetLayerNoRestore( int iLayer, bool bNoRestore ) } -#ifdef MAPBASE // From Alien Swarm SDK +#ifdef MAPBASE //----------------------------------------------------------------------------- -// Purpose: +// From Alien Swarm SDK //----------------------------------------------------------------------------- void CBaseAnimatingOverlay::SetLayerNoEvents( int iLayer, bool bNoEvents ) { @@ -1113,6 +1113,17 @@ void CBaseAnimatingOverlay::SetLayerNoEvents( int iLayer, bool bNoEvents ) m_AnimOverlay[iLayer].m_fFlags &= ~ANIM_LAYER_NOEVENTS; } } + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseAnimatingOverlay::IsLayerFinished( int iLayer ) +{ + if (!IsValidLayer( iLayer )) + return true; + + return m_AnimOverlay[iLayer].m_bSequenceFinished; +} #endif diff --git a/sp/src/game/server/BaseAnimatingOverlay.h b/sp/src/game/server/BaseAnimatingOverlay.h index d7b882f8..38ee7ed1 100644 --- a/sp/src/game/server/BaseAnimatingOverlay.h +++ b/sp/src/game/server/BaseAnimatingOverlay.h @@ -181,8 +181,10 @@ public: void SetLayerAutokill( int iLayer, bool bAutokill ); void SetLayerLooping( int iLayer, bool bLooping ); void SetLayerNoRestore( int iLayer, bool bNoRestore ); -#ifdef MAPBASE // From Alien Swarm SDK - void SetLayerNoEvents( int iLayer, bool bNoEvents ); +#ifdef MAPBASE + void SetLayerNoEvents( int iLayer, bool bNoEvents ); // From Alien Swarm SDK + + bool IsLayerFinished( int iLayer ); #endif Activity GetLayerActivity( int iLayer ); diff --git a/sp/src/game/server/BasePropDoor.h b/sp/src/game/server/BasePropDoor.h index d96c75c5..2557bec3 100644 --- a/sp/src/game/server/BasePropDoor.h +++ b/sp/src/game/server/BasePropDoor.h @@ -103,6 +103,12 @@ protected: inline CBaseEntity *GetActivator(); +#ifdef MAPBASE + inline float GetNPCOpenDistance() { return m_flNPCOpenDistance; } + inline Activity GetNPCOpenFrontActivity() { return m_eNPCOpenFrontActivity; } + inline Activity GetNPCOpenBackActivity() { return m_eNPCOpenBackActivity; } +#endif + private: // Implement these in your leaf class. @@ -196,6 +202,12 @@ private: string_t m_SoundOpen; string_t m_SoundClose; +#ifdef MAPBASE + float m_flNPCOpenDistance; + Activity m_eNPCOpenFrontActivity; + Activity m_eNPCOpenBackActivity; +#endif + // dvs: FIXME: can we remove m_flSpeed from CBaseEntity? //float m_flSpeed; // Rotation speed when opening or closing in degrees per second. diff --git a/sp/src/game/server/ai_activity.cpp b/sp/src/game/server/ai_activity.cpp index b88c1142..02afb3b7 100644 --- a/sp/src/game/server/ai_activity.cpp +++ b/sp/src/game/server/ai_activity.cpp @@ -2180,15 +2180,22 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_AR2 ); ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2 ); - //ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_AR2_LOW ); ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_AR2 ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_AR2_LOW ); #endif #ifdef SHARED_COMBINE_ACTIVITIES ADD_ACTIVITY_TO_SR( ACT_COMBINE_THROW_GRENADE ); ADD_ACTIVITY_TO_SR( ACT_COMBINE_AR2_ALTFIRE ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_COMBINE_THROW_GRENADE ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SPECIAL_ATTACK1 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SPECIAL_ATTACK2 ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_ADVANCE ); ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_FORWARD ); ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_GROUP ); @@ -2198,8 +2205,249 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_GESTURE_SIGNAL_TAKECOVER ); #endif -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_IDLE_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_WALK_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RUN_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_REVOLVER_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_REVOLVER_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_REVOLVER_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_REVOLVER ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_REVOLVER ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_ANGRY_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_WALK_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RUN_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RELOAD_CROSSBOW_LOW ); + ADD_ACTIVITY_TO_SR( ACT_COVER_CROSSBOW_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_CROSSBOW_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_CROSSBOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RELOAD_CROSSBOW ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_PISTOL_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_IDLE_PISTOL_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_PISTOL_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_PISTOL_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_PISTOL_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_PISTOL_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_PISTOL_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_PISTOL_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_PISTOL_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_WALK_CROUCH_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_WALK_CROUCH_AIM_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_RUN_CROUCH_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_RUN_CROUCH_AIM_PISTOL ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_SHOTGUN_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SHOTGUN_LOW ); + + ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_SHOTGUN_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN_RELAXED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SHOTGUN_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_IDLE_AIM_SHOTGUN_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_WALK_AIM_SHOTGUN_STIMULATED ); + ADD_ACTIVITY_TO_SR( ACT_RUN_AIM_SHOTGUN_STIMULATED ); + + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_RPG_LOW ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_RPG_LOW ); + ADD_ACTIVITY_TO_SR( ACT_GESTURE_RANGE_ATTACK_RPG ); + + ADD_ACTIVITY_TO_SR( ACT_WALK_MELEE ); + ADD_ACTIVITY_TO_SR( ACT_RUN_MELEE ); + + ADD_ACTIVITY_TO_SR( ACT_RUN_PACKAGE ); + ADD_ACTIVITY_TO_SR( ACT_RUN_SUITCASE ); + ADD_ACTIVITY_TO_SR( ACT_ARM_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_ARM_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_ARM_RPG ); + ADD_ACTIVITY_TO_SR( ACT_ARM_MELEE ); ADD_ACTIVITY_TO_SR( ACT_DISARM_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_DISARM_SHOTGUN ); + ADD_ACTIVITY_TO_SR( ACT_DISARM_RPG ); + ADD_ACTIVITY_TO_SR( ACT_DISARM_MELEE ); +#endif + +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_CLIMB_ALL ); + ADD_ACTIVITY_TO_SR( ACT_CLIMB_IDLE ); + + ADD_ACTIVITY_TO_SR( ACT_CLIMB_MOUNT_TOP ); + ADD_ACTIVITY_TO_SR( ACT_CLIMB_MOUNT_BOTTOM ); + ADD_ACTIVITY_TO_SR( ACT_CLIMB_DISMOUNT_BOTTOM ); +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK2_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_MED ); + + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_AR2_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SMG1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_SHOTGUN_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_PISTOL_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_RPG_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_REVOLVER_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_ATTACK_CROSSBOW_MED ); + + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_AR2_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SMG1_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_SHOTGUN_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_PISTOL_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_RPG_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_REVOLVER_MED ); + ADD_ACTIVITY_TO_SR( ACT_RANGE_AIM_CROSSBOW_MED ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R_RIFLE ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L_RIFLE ); + + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_R_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_L_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_R_PISTOL ); + ADD_ACTIVITY_TO_SR( ACT_COVER_WALL_LOW_L_PISTOL ); #endif } + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: This is a multi-purpose table which links NPC activities to their gesture variants. +//----------------------------------------------------------------------------- +CAI_BaseNPC::actlink_t CAI_BaseNPC::gm_ActivityGestureLinks[] = +{ + { ACT_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK1 }, + { ACT_RANGE_ATTACK2, ACT_GESTURE_RANGE_ATTACK2 }, + { ACT_MELEE_ATTACK1, ACT_GESTURE_MELEE_ATTACK1 }, + { ACT_MELEE_ATTACK2, ACT_GESTURE_MELEE_ATTACK2 }, + { ACT_RELOAD, ACT_GESTURE_RELOAD }, + + { ACT_RANGE_ATTACK1_LOW, ACT_GESTURE_RANGE_ATTACK1 }, // NOTE: ACT_GESTURE_RANGE_ATTACK1_LOW exists, but isn't used + { ACT_RANGE_ATTACK2_LOW, ACT_GESTURE_RANGE_ATTACK2 }, // NOTE: ACT_GESTURE_RANGE_ATTACK2_LOW exists, but isn't used + { ACT_RELOAD_LOW, ACT_GESTURE_RELOAD }, + + { ACT_MELEE_ATTACK_SWING, ACT_GESTURE_MELEE_ATTACK_SWING }, + + // ----------------------------------------------------------- + + { ACT_RANGE_ATTACK_AR2, ACT_GESTURE_RANGE_ATTACK_AR2 }, + { ACT_RANGE_ATTACK_AR2_LOW, ACT_GESTURE_RANGE_ATTACK_AR2 }, + { ACT_RANGE_ATTACK_SMG1, ACT_GESTURE_RANGE_ATTACK_SMG1 }, + { ACT_RANGE_ATTACK_SMG1_LOW, ACT_GESTURE_RANGE_ATTACK_SMG1 }, + { ACT_RANGE_ATTACK_SHOTGUN, ACT_GESTURE_RANGE_ATTACK_SHOTGUN }, + { ACT_RANGE_ATTACK_SHOTGUN_LOW, ACT_GESTURE_RANGE_ATTACK_SHOTGUN }, + { ACT_RANGE_ATTACK_PISTOL, ACT_GESTURE_RANGE_ATTACK_PISTOL }, + { ACT_RANGE_ATTACK_PISTOL_LOW, ACT_GESTURE_RANGE_ATTACK_PISTOL }, + + // ----------------------------------------------------------- + + { ACT_SMALL_FLINCH, ACT_GESTURE_SMALL_FLINCH }, + { ACT_BIG_FLINCH, ACT_GESTURE_BIG_FLINCH }, + { ACT_FLINCH_HEAD, ACT_GESTURE_FLINCH_HEAD }, + { ACT_FLINCH_CHEST, ACT_GESTURE_FLINCH_CHEST }, + { ACT_FLINCH_STOMACH, ACT_GESTURE_FLINCH_STOMACH }, + { ACT_FLINCH_LEFTARM, ACT_GESTURE_FLINCH_LEFTARM }, + { ACT_FLINCH_RIGHTARM, ACT_GESTURE_FLINCH_RIGHTARM }, + { ACT_FLINCH_LEFTLEG, ACT_GESTURE_FLINCH_LEFTLEG }, + { ACT_FLINCH_RIGHTLEG, ACT_GESTURE_FLINCH_RIGHTLEG }, + + // ----------------------------------------------------------- + +#if AR2_ACTIVITY_FIX == 1 + { ACT_RELOAD_AR2, ACT_GESTURE_RELOAD_AR2 }, + { ACT_RELOAD_AR2_LOW, ACT_GESTURE_RELOAD_AR2 }, +#endif + { ACT_RELOAD_SMG1, ACT_GESTURE_RELOAD_SMG1 }, + { ACT_RELOAD_SMG1_LOW, ACT_GESTURE_RELOAD_SMG1 }, + { ACT_RELOAD_SHOTGUN, ACT_GESTURE_RELOAD_SHOTGUN }, + { ACT_RELOAD_SHOTGUN_LOW, ACT_GESTURE_RELOAD_SHOTGUN }, + { ACT_RELOAD_PISTOL, ACT_GESTURE_RELOAD_PISTOL }, + { ACT_RELOAD_PISTOL_LOW, ACT_GESTURE_RELOAD_PISTOL }, + +#ifdef SHARED_COMBINE_ACTIVITIES + { ACT_SPECIAL_ATTACK1, ACT_GESTURE_SPECIAL_ATTACK1 }, + { ACT_SPECIAL_ATTACK2, ACT_GESTURE_SPECIAL_ATTACK2 }, + { ACT_COMBINE_THROW_GRENADE, ACT_GESTURE_COMBINE_THROW_GRENADE }, + { ACT_COMBINE_AR2_ALTFIRE, ACT_GESTURE_COMBINE_AR2_ALTFIRE }, + + { ACT_SIGNAL_ADVANCE, ACT_GESTURE_SIGNAL_ADVANCE }, + { ACT_SIGNAL_FORWARD, ACT_GESTURE_SIGNAL_FORWARD }, + { ACT_SIGNAL_GROUP, ACT_GESTURE_SIGNAL_GROUP }, + { ACT_SIGNAL_HALT, ACT_GESTURE_SIGNAL_HALT }, + { ACT_SIGNAL_LEFT, ACT_GESTURE_SIGNAL_LEFT }, + { ACT_SIGNAL_RIGHT, ACT_GESTURE_SIGNAL_RIGHT }, + { ACT_SIGNAL_TAKECOVER, ACT_GESTURE_SIGNAL_TAKECOVER }, +#endif + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_ATTACK_REVOLVER, ACT_GESTURE_RANGE_ATTACK_REVOLVER }, + { ACT_RANGE_ATTACK_REVOLVER_LOW, ACT_GESTURE_RANGE_ATTACK_REVOLVER }, + { ACT_RANGE_ATTACK_CROSSBOW, ACT_GESTURE_RANGE_ATTACK_CROSSBOW }, + { ACT_RANGE_ATTACK_CROSSBOW_LOW, ACT_GESTURE_RANGE_ATTACK_CROSSBOW }, + { ACT_RANGE_ATTACK_RPG, ACT_GESTURE_RANGE_ATTACK_RPG }, + { ACT_RANGE_ATTACK_RPG_LOW, ACT_GESTURE_RANGE_ATTACK_RPG }, + + { ACT_RELOAD_REVOLVER, ACT_GESTURE_RELOAD_REVOLVER }, + { ACT_RELOAD_REVOLVER_LOW, ACT_GESTURE_RELOAD_REVOLVER }, + { ACT_RELOAD_CROSSBOW, ACT_GESTURE_RELOAD_CROSSBOW }, + { ACT_RELOAD_CROSSBOW_LOW, ACT_GESTURE_RELOAD_CROSSBOW }, +#endif +}; + +Activity CAI_BaseNPC::GetGestureVersionOfActivity( Activity inActivity ) +{ + actlink_t *pTable = gm_ActivityGestureLinks; + int actCount = ARRAYSIZE( gm_ActivityGestureLinks ); + + for ( int i = 0; i < actCount; i++, pTable++ ) + { + if ( inActivity == pTable->sequence ) + { + return pTable->gesture; + } + } + + return ACT_INVALID; +} + +Activity CAI_BaseNPC::GetSequenceVersionOfGesture( Activity inActivity ) +{ + actlink_t *pTable = gm_ActivityGestureLinks; + int actCount = ARRAYSIZE( gm_ActivityGestureLinks ); + + for (int i = 0; i < actCount; i++, pTable++) + { + if (inActivity == pTable->gesture) + { + return pTable->sequence; + } + } + + return ACT_INVALID; +} +#endif diff --git a/sp/src/game/server/ai_basenpc.cpp b/sp/src/game/server/ai_basenpc.cpp index d2ec72bf..7167b2c8 100644 --- a/sp/src/game/server/ai_basenpc.cpp +++ b/sp/src/game/server/ai_basenpc.cpp @@ -169,6 +169,8 @@ extern ConVar ai_vehicle_avoidance; extern ISoundEmitterSystemBase *soundemitterbase; ConVar ai_dynint_always_enabled( "ai_dynint_always_enabled", "0", FCVAR_NONE, "Makes the \"Don't Care\" setting equivalent to \"Yes\"." ); + +ConVar ai_debug_fake_sequence_gestures_always_play( "ai_debug_fake_sequence_gestures_always_play", "0", FCVAR_NONE, "Always plays fake sequence gestures." ); #endif #ifndef _RETAIL @@ -313,6 +315,8 @@ ScriptHook_t CAI_BaseNPC::g_Hook_QuerySeeEntity; ScriptHook_t CAI_BaseNPC::g_Hook_TranslateActivity; ScriptHook_t CAI_BaseNPC::g_Hook_TranslateSchedule; ScriptHook_t CAI_BaseNPC::g_Hook_GetActualShootPosition; +ScriptHook_t CAI_BaseNPC::g_Hook_OverrideMove; +ScriptHook_t CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture; #endif // @@ -2594,7 +2598,11 @@ bool CAI_BaseNPC::FValidateHintType ( CAI_Hint *pHint ) Activity CAI_BaseNPC::GetHintActivity( short sHintType, Activity HintsActivity ) { if ( HintsActivity != ACT_INVALID ) +#ifdef MAPBASE + return TranslateActivity( HintsActivity ); // Always translate the activity +#else return HintsActivity; +#endif return ACT_IDLE; } @@ -6509,7 +6517,7 @@ CAI_BaseNPC *CAI_BaseNPC::CreateCustomTarget( const Vector &vecOrigin, float dur //----------------------------------------------------------------------------- Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity ) { - if (CapabilitiesGet() & bits_CAP_DUCK) + if (CapabilitiesGet() & bits_CAP_DUCK && CanTranslateCrouchActivity()) { // ======================================================================== // The main issue with cover hint nodes is that crouch activities are not translated at the right time @@ -6541,10 +6549,18 @@ Activity CAI_BaseNPC::TranslateCrouchActivity( Activity eNewActivity ) CAI_Hint *pHint = GetHintNode(); if (pHint) { - if (pHint->HintType() == HINT_TACTICAL_COVER_LOW || pHint->HintType() == HINT_TACTICAL_COVER_MED) + if (pHint->HintType() == HINT_TACTICAL_COVER_LOW) { nCoverActivity = ACT_RANGE_ATTACK1_LOW; } + else if (pHint->HintType() == HINT_TACTICAL_COVER_MED) + { +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + nCoverActivity = ACT_RANGE_ATTACK1_MED; +#else + nCoverActivity = ACT_RANGE_ATTACK1_LOW; +#endif + } } } } @@ -6586,15 +6602,30 @@ Activity CAI_BaseNPC::NPC_BackupActivity( Activity eNewActivity ) //if (eNewActivity == ACT_DROP_WEAPON) // return TranslateActivity(ACT_IDLE); + // --------------------------------------------- + // Accounts for certain act busy activities that aren't on all NPCs. if (eNewActivity == ACT_BUSY_QUEUE || eNewActivity == ACT_BUSY_STAND) return TranslateActivity(ACT_IDLE); + // --------------------------------------------- + if (eNewActivity == ACT_WALK_ANGRY) return TranslateActivity(ACT_WALK); - // GetCoverActivity() should have this covered. // --------------------------------------------- + + // If one climbing animation isn't available, use the other + if (eNewActivity == ACT_CLIMB_DOWN) + return ACT_CLIMB_UP; + else if (eNewActivity == ACT_CLIMB_UP) + return ACT_CLIMB_DOWN; + + // --------------------------------------------- + + if (eNewActivity == ACT_COVER_MED) + eNewActivity = ACT_COVER_LOW; + //if (eNewActivity == ACT_COVER) // return TranslateActivity(ACT_IDLE); @@ -6609,6 +6640,14 @@ Activity CAI_BaseNPC::NPC_BackupActivity( Activity eNewActivity ) //----------------------------------------------------------------------------- Activity CAI_BaseNPC::NPC_TranslateActivity( Activity eNewActivity ) { +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + if ( GetNavType() == NAV_CLIMB && eNewActivity == ACT_IDLE ) + { + // Schedules which break into idle activities should try to maintain the climbing animation. + return ACT_CLIMB_IDLE; + } +#endif + #ifdef MAPBASE Assert( eNewActivity != ACT_INVALID ); @@ -6831,7 +6870,12 @@ void CAI_BaseNPC::ResolveActivityToSequence(Activity NewActivity, int &iSequence translatedActivity = TranslateActivity( NewActivity, &weaponActivity ); +#ifdef MAPBASE + // Cover cases where TranslateActivity() returns a sequence by using ACT_SCRIPT_CUSTOM_MOVE + if ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE || translatedActivity == ACT_SCRIPT_CUSTOM_MOVE ) +#else if ( NewActivity == ACT_SCRIPT_CUSTOM_MOVE ) +#endif { iSequence = GetScriptCustomMoveSequence(); } @@ -7017,6 +7061,23 @@ void CAI_BaseNPC::SetIdealActivity( Activity NewActivity ) // Perform translation in case we need to change sequences within a single activity, // such as between a standing idle and a crouching idle. ResolveActivityToSequence(m_IdealActivity, m_nIdealSequence, m_IdealTranslatedActivity, m_IdealWeaponActivity); + +#ifdef MAPBASE + // Check if we need a gesture to imitate this sequence + if ( ShouldPlayFakeSequenceGesture( m_IdealActivity, m_IdealTranslatedActivity ) ) + { + Activity nGesture = SelectFakeSequenceGesture( m_IdealActivity, m_IdealTranslatedActivity ); + if (nGesture != -1) + { + PlayFakeSequenceGesture( nGesture, m_IdealActivity, m_IdealTranslatedActivity ); + } + } + else if (GetFakeSequenceGesture() != -1) + { + // Reset the current gesture sequence if there is one + ResetFakeSequenceGesture(); + } +#endif } @@ -7065,6 +7126,14 @@ void CAI_BaseNPC::AdvanceToIdealActivity(void) //DevMsg("%s: Unable to get from sequence %s to %s!\n", GetClassname(), GetSequenceName(GetSequence()), GetSequenceName(m_nIdealSequence)); SetActivity(m_IdealActivity); } + +#ifdef MAPBASE + // If there was a gesture imitating a sequence, get rid of it + if ( GetFakeSequenceGesture() != -1 ) + { + ResetFakeSequenceGesture(); + } +#endif } @@ -7122,6 +7191,12 @@ void CAI_BaseNPC::MaintainActivity(void) } // Else a transition sequence is in progress, do nothing. } +#ifdef MAPBASE + else if (GetFakeSequenceGesture() != -1) + { + // Don't advance even if the sequence gesture is finished, as advancing would just play the original activity afterwards + } +#endif // Else get a specific sequence for the activity and try to transition to that. else { @@ -7140,11 +7215,103 @@ void CAI_BaseNPC::MaintainActivity(void) } +#ifdef MAPBASE +bool CAI_BaseNPC::ShouldPlayFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity ) +{ + // Don't do anything if we're resetting our activity + if (GetActivity() == ACT_RESET) + return false; + + // No need to do this while we're moving + if (IsCurTaskContinuousMove()) + return false; + + if (ai_debug_fake_sequence_gestures_always_play.GetBool()) + return true; + +#ifdef MAPBASE_VSCRIPT + if (m_ScriptScope.IsInitialized() && g_Hook_ShouldPlayFakeSequenceGesture.CanRunInScope(m_ScriptScope)) + { + // activity, translatedActivity + ScriptVariant_t functionReturn; + ScriptVariant_t args[] = { GetActivityName( nActivity ), GetActivityName( nTranslatedActivity ) }; + if (g_Hook_ShouldPlayFakeSequenceGesture.Call( m_ScriptScope, &functionReturn, args )) + { + if (functionReturn.m_type == FIELD_BOOLEAN) + return functionReturn.m_bool; + } + } +#endif + + if (GetHintNode() && GetHintNode()->HintActivityName() != NULL_STRING) + { + switch (GetHintNode()->HintType()) + { + // Cover nodes with custom activities should allow NPCs to do things like reload while in cover. + case HINT_TACTICAL_COVER_LOW: + case HINT_TACTICAL_COVER_MED: + case HINT_TACTICAL_COVER_CUSTOM: + if (HasMemory( bits_MEMORY_INCOVER )) + { + // Don't attack while using a custom animation in cover + if (nActivity != ACT_RANGE_ATTACK1 && nActivity != ACT_RANGE_ATTACK1_LOW) + return true; + } + break; + } + } + + return false; +} + +Activity CAI_BaseNPC::SelectFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity ) +{ + return GetGestureVersionOfActivity( nTranslatedActivity ); +} + +inline void CAI_BaseNPC::PlayFakeSequenceGesture( Activity nActivity, Activity nSequence, Activity nTranslatedSequence ) +{ + RestartGesture( nActivity, true, true ); + m_FakeSequenceGestureLayer = FindGestureLayer( nActivity ); + + switch ( nSequence ) + { + case ACT_RANGE_ATTACK1: + //case ACT_RANGE_ATTACK2: + { + OnRangeAttack1(); + + // FIXME: this seems a bit wacked + Weapon_SetActivity( Weapon_TranslateActivity( nSequence ), 0 ); + } break; + } +} + +inline int CAI_BaseNPC::GetFakeSequenceGesture() +{ + return m_FakeSequenceGestureLayer; +} + +void CAI_BaseNPC::ResetFakeSequenceGesture() +{ + SetLayerCycle( m_FakeSequenceGestureLayer, 1.0f ); + m_FakeSequenceGestureLayer = -1; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Returns true if our ideal activity has finished playing. //----------------------------------------------------------------------------- bool CAI_BaseNPC::IsActivityFinished( void ) { +#ifdef MAPBASE + if (GetFakeSequenceGesture() != -1) + { + return IsLayerFinished( GetFakeSequenceGesture() ); + } +#endif + return (IsSequenceFinished() && (GetSequence() == m_nIdealSequence)); } @@ -7180,6 +7347,15 @@ void CAI_BaseNPC::OnChangeActivity( Activity eNewActivity ) eNewActivity == ACT_WALK ) { Stand(); + +#ifdef MAPBASE + // Unlock custom cover nodes + if (GetHintNode() && GetHintNode()->HintType() == HINT_TACTICAL_COVER_CUSTOM && HasMemory(bits_MEMORY_INCOVER)) + { + GetHintNode()->Unlock( GetHintDelay( GetHintNode()->HintType() ) ); + SetHintNode( NULL ); + } +#endif } } @@ -7768,7 +7944,7 @@ int CAI_BaseNPC::HolsterWeapon( void ) if ( IsWeaponHolstered() ) return -1; -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef MAPBASE Activity activity = TranslateActivity( ACT_DISARM ); int iHolsterGesture = FindGestureLayer( activity ); if ( iHolsterGesture != -1 ) @@ -7824,7 +8000,7 @@ int CAI_BaseNPC::UnholsterWeapon( void ) if ( !IsWeaponHolstered() ) return -1; -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef MAPBASE Activity activity = TranslateActivity( ACT_ARM ); int iHolsterGesture = FindGestureLayer( activity ); #else @@ -7853,13 +8029,12 @@ int CAI_BaseNPC::UnholsterWeapon( void ) { SetActiveWeapon( GetWeapon(i) ); -#ifdef COMPANION_HOLSTER_WORKAROUND - int iLayer = AddGesture( activity, true ); - //iLayer = AddGesture( ACT_GESTURE_ARM, true ); +#ifdef MAPBASE + int iLayer = AddGesture( TranslateActivity( ACT_ARM ), true ); #else int iLayer = AddGesture( ACT_ARM, true ); - //iLayer = AddGesture( ACT_GESTURE_ARM, true ); #endif + //iLayer = AddGesture( ACT_GESTURE_ARM, true ); if (iLayer != -1) { @@ -8038,6 +8213,26 @@ bool CAI_BaseNPC::DoUnholster( void ) return false; } +//----------------------------------------------------------------------------- +// Purpose: Returns true if the NPC should be unholstering their weapon +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::ShouldUnholsterWeapon( void ) +{ + return GetState() == NPC_STATE_COMBAT; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns true if the NPC can unholster their weapon +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::CanUnholsterWeapon( void ) +{ + // Don't unholster during special navigation + if ( GetNavType() == NAV_JUMP || GetNavType() == NAV_CLIMB ) + return false; + + return IsWeaponHolstered(); +} + //------------------------------------------------------------------------------ // Purpose: Give the NPC in question the weapon specified //------------------------------------------------------------------------------ @@ -9104,27 +9299,45 @@ Activity CAI_BaseNPC::GetCoverActivity( CAI_Hint *pHint ) switch (pHint->HintType()) { case HINT_TACTICAL_COVER_MED: -#ifndef MAPBASE // I know what you're thinking, but ACT_COVER_MED is pretty much deprecated at this point anyway. { - nCoverActivity = ACT_COVER_MED; #ifdef MAPBASE - // Some NPCs lack ACT_COVER_MED, but could easily use ACT_COVER_LOW. - if (SelectWeightedSequence(nCoverActivity) == ACTIVITY_NOT_AVAILABLE) - nCoverActivity = ACT_COVER_LOW; -#endif + // NPCs which lack ACT_COVER_MED should fall through to HINT_TACTICAL_COVER_LOW + if (SelectWeightedSequence( ACT_COVER_MED ) != ACTIVITY_NOT_AVAILABLE) + { + nCoverActivity = ACT_COVER_MED; + } +#else + nCoverActivity = ACT_COVER_MED; break; - } #endif + } case HINT_TACTICAL_COVER_LOW: { #ifdef MAPBASE - if (pHint->HintActivityName() != NULL_STRING) - nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) ); - else -#endif + // Make sure nCoverActivity wasn't already assigned above, then fall through to HINT_TACTICAL_COVER_CUSTOM + if (nCoverActivity == ACT_INVALID) + nCoverActivity = ACT_COVER_LOW; +#else nCoverActivity = ACT_COVER_LOW; break; +#endif } + +#ifdef MAPBASE + case HINT_TACTICAL_COVER_CUSTOM: + { + if (pHint->HintActivityName() != NULL_STRING) + { + nCoverActivity = (Activity)CAI_BaseNPC::GetActivityID( STRING(pHint->HintActivityName()) ); + if (nCoverActivity == ACT_INVALID) + { + m_iszSceneCustomMoveSeq = pHint->HintActivityName(); + nCoverActivity = ACT_SCRIPT_CUSTOM_MOVE; + } + } + break; + } +#endif } } @@ -9162,6 +9375,13 @@ float CAI_BaseNPC::CalcIdealYaw( const Vector &vecTarget ) return UTIL_VecToYaw( vecProjection - GetLocalOrigin() ); } +#ifdef MAPBASE + // Allow hint nodes to override the yaw without needing to control AI + else if (GetHintNode() && GetHintNode()->OverridesNPCYaw( this )) + { + return GetHintNode()->Yaw(); + } +#endif else { return UTIL_VecToYaw ( vecTarget - GetLocalOrigin() ); @@ -9502,7 +9722,7 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) if ( GetActiveWeapon() ) { #ifdef MAPBASE - GetActiveWeapon()->Reload_NPC(); + GetActiveWeapon()->Reload_NPC( true ); #else GetActiveWeapon()->WeaponSound( RELOAD_NPC ); GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); @@ -9526,8 +9746,12 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) case EVENT_WEAPON_RELOAD_FILL_CLIP: { if ( GetActiveWeapon() ) - { + { +#ifdef MAPBASE + GetActiveWeapon()->Reload_NPC( false ); +#else GetActiveWeapon()->m_iClip1 = GetActiveWeapon()->GetMaxClip1(); +#endif ClearCondition(COND_LOW_PRIMARY_AMMO); ClearCondition(COND_NO_PRIMARY_AMMO); ClearCondition(COND_NO_SECONDARY_AMMO); @@ -9542,7 +9766,11 @@ void CAI_BaseNPC::HandleAnimEvent( animevent_t *pEvent ) case NPC_EVENT_OPEN_DOOR: { +#ifdef MAPBASE + CBasePropDoor *pDoor = m_hOpeningDoor; +#else CBasePropDoor *pDoor = (CBasePropDoor *)(CBaseEntity *)GetNavigator()->GetPath()->GetCurWaypoint()->GetEHandleData(); +#endif if (pDoor != NULL) { OpenPropDoorNow( pDoor ); @@ -11904,6 +12132,7 @@ BEGIN_DATADESC( CAI_BaseNPC ) DEFINE_KEYFIELD( m_FriendlyFireOverride, FIELD_INTEGER, "FriendlyFireOverride" ), DEFINE_KEYFIELD( m_flSpeedModifier, FIELD_FLOAT, "BaseSpeedModifier" ), + DEFINE_FIELD( m_FakeSequenceGestureLayer, FIELD_INTEGER ), #endif // Satisfy classcheck @@ -12061,6 +12290,11 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC DEFINE_SCRIPTFUNC_NAMED( ScriptSetActivityID, "SetActivityID", "Set the NPC's current activity ID." ) DEFINE_SCRIPTFUNC( ResetActivity, "Reset the NPC's current activity." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetGestureVersionOfActivity, "GetGestureVersionOfActivity", "Get the gesture activity counterpart of the specified sequence activity, if one exists." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetGestureVersionOfActivityID, "GetGestureVersionOfActivityID", "Get the gesture activity ID counterpart of the specified sequence activity ID, if one exists." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSequenceVersionOfGesture, "GetSequenceVersionOfGesture", "Get the sequence activity counterpart of the specified gesture activity, if one exists." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSequenceVersionOfGestureID, "GetSequenceVersionOfGestureID", "Get the sequence activity ID counterpart of the specified gesture activity ID, if one exists." ) + DEFINE_SCRIPTFUNC_NAMED( VScriptGetSchedule, "GetSchedule", "Get the NPC's current schedule." ) DEFINE_SCRIPTFUNC_NAMED( VScriptGetScheduleID, "GetScheduleID", "Get the NPC's current schedule ID." ) DEFINE_SCRIPTFUNC_NAMED( VScriptSetSchedule, "SetSchedule", "Set the NPC's current schedule." ) @@ -12107,10 +12341,17 @@ BEGIN_ENT_SCRIPTDESC( CAI_BaseNPC, CBaseCombatCharacter, "The base class all NPC DEFINE_SCRIPTHOOK_PARAM( "schedule", FIELD_CSTRING ) DEFINE_SCRIPTHOOK_PARAM( "schedule_id", FIELD_INTEGER ) END_SCRIPTHOOK() - BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_GetActualShootPosition, "GetActualShootPosition", FIELD_VOID, "Called when the NPC is getting their actual shoot position, using the default shoot position as the parameter. (NOTE: NPCs which override this themselves might not always use this hook!)" ) + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_GetActualShootPosition, "GetActualShootPosition", FIELD_VECTOR, "Called when the NPC is getting their actual shoot position, using the default shoot position as the parameter. (NOTE: NPCs which override this themselves might not always use this hook!)" ) DEFINE_SCRIPTHOOK_PARAM( "shootOrigin", FIELD_VECTOR ) DEFINE_SCRIPTHOOK_PARAM( "target", FIELD_HSCRIPT ) END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_OverrideMove, "OverrideMove", FIELD_VOID, "Called when the NPC runs movement code, allowing the NPC's movement to be overridden by some other method. (NOTE: NPCs which override this themselves might not always use this hook!)" ) + DEFINE_SCRIPTHOOK_PARAM( "interval", FIELD_FLOAT ) + END_SCRIPTHOOK() + BEGIN_SCRIPTHOOK( CAI_BaseNPC::g_Hook_ShouldPlayFakeSequenceGesture, "ShouldPlayFakeSequenceGesture", FIELD_BOOLEAN, "Called when an activity is set on a NPC. Returning true will make the NPC convert the activity into a gesture (if a gesture is available) and continue their current activity instead." ) + DEFINE_SCRIPTHOOK_PARAM( "activity", FIELD_CSTRING ) + DEFINE_SCRIPTHOOK_PARAM( "translatedActivity", FIELD_CSTRING ) + END_SCRIPTHOOK() END_SCRIPTDESC(); #endif @@ -12761,6 +13002,8 @@ CAI_BaseNPC::CAI_BaseNPC(void) #ifdef MAPBASE m_iDynamicInteractionsAllowed = TRS_NONE; m_flSpeedModifier = 1.0f; + + m_FakeSequenceGestureLayer = -1; #endif } @@ -13715,6 +13958,10 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal, GetNavigator()->GetPath()->PrependWaypoints( pOpenDoorRoute ); +#ifdef MAPBASE + GetNavigator()->SetArrivalDirection( opendata.vecFaceDir ); +#endif + m_hOpeningDoor = pDoor; pMoveGoal->maxDist = distClear; *pResult = AIMR_CHANGE_TYPE; @@ -13735,6 +13982,21 @@ bool CAI_BaseNPC::OnUpcomingPropDoor( AILocalMoveGoal_t *pMoveGoal, //----------------------------------------------------------------------------- void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor ) { +#ifdef MAPBASE + opendata_t opendata; + pDoor->GetNPCOpenData(this, opendata); + + if (HaveSequenceForActivity( opendata.eActivity )) + { + int iLayer = AddGesture( opendata.eActivity ); + float flDuration = GetLayerDuration( iLayer ); + + // Face the door and wait for the activity to finish before trying to move through the doorway. + m_flMoveWaitFinished = gpGlobals->curtime + flDuration + pDoor->GetOpenInterval(); + AddFacingTarget( opendata.vecFaceDir, 1.0, flDuration ); + } + else +#else // dvs: not quite working, disabled for now. //opendata_t opendata; //pDoor->GetNPCOpenData(this, opendata); @@ -13744,6 +14006,7 @@ void CAI_BaseNPC::OpenPropDoorBegin( CBasePropDoor *pDoor ) // SetIdealActivity(opendata.eActivity); //} //else +#endif { // We don't have an appropriate sequence, just open the door magically. OpenPropDoorNow( pDoor ); @@ -13763,6 +14026,15 @@ void CAI_BaseNPC::OpenPropDoorNow( CBasePropDoor *pDoor ) // Wait for the door to finish opening before trying to move through the doorway. m_flMoveWaitFinished = gpGlobals->curtime + pDoor->GetOpenInterval(); + +#ifdef MAPBASE + // Remove the door from our waypoint + if (GetNavigator()->GetPath() && GetNavigator()->GetCurWaypointFlags() & bits_WP_TO_DOOR) + { + GetNavigator()->GetPath()->GetCurWaypoint()->ModifyFlags( bits_WP_TO_DOOR, false ); + GetNavigator()->GetPath()->GetCurWaypoint()->m_hData = NULL; + } +#endif } @@ -15952,6 +16224,26 @@ bool CAI_BaseNPC::CouldShootIfCrouching( CBaseEntity *pTarget ) return bResult; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Check if this position will block our line of sight if aiming low. +//----------------------------------------------------------------------------- +bool CAI_BaseNPC::CouldShootIfCrouchingAt( const Vector &vecPosition, const Vector &vecForward, const Vector &vecRight, float flDist ) +{ + Vector vGunPos = vecPosition; + vGunPos += (GetCrouchGunOffset() + vecRight * 8); + + trace_t tr; + AI_TraceLOS( vGunPos, vGunPos + (vecForward * flDist), this, &tr ); + if (tr.fraction != 1.0) + { + return false; + } + + return true; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -15967,9 +16259,16 @@ bool CAI_BaseNPC::IsCrouchedActivity( Activity activity ) case ACT_COVER_SMG1_LOW: case ACT_RELOAD_SMG1_LOW: #ifdef MAPBASE - //case ACT_RELOAD_AR2_LOW: +#if AR2_ACTIVITY_FIX == 1 + case ACT_COVER_AR2_LOW: + case ACT_RELOAD_AR2_LOW: +#endif case ACT_RELOAD_PISTOL_LOW: case ACT_RELOAD_SHOTGUN_LOW: +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_RELOAD_REVOLVER_LOW: + case ACT_RELOAD_CROSSBOW_LOW: +#endif #endif return true; } diff --git a/sp/src/game/server/ai_basenpc.h b/sp/src/game/server/ai_basenpc.h index 1a00cb87..fe63f36e 100644 --- a/sp/src/game/server/ai_basenpc.h +++ b/sp/src/game/server/ai_basenpc.h @@ -998,6 +998,7 @@ public: Activity NPC_TranslateActivity( Activity eNewActivity ); #ifdef MAPBASE Activity TranslateCrouchActivity( Activity baseAct ); + virtual bool CanTranslateCrouchActivity( void ) { return true; } virtual Activity NPC_BackupActivity( Activity eNewActivity ); #endif Activity GetActivity( void ) { return m_Activity; } @@ -1020,6 +1021,25 @@ public: void SetActivityAndSequence(Activity NewActivity, int iSequence, Activity translatedActivity, Activity weaponActivity); +#ifdef MAPBASE + //----------------------------------------------------- + + // Returns the gesture variant of an activity (i.e. "ACT_GESTURE_RANGE_ATTACK1") + static Activity GetGestureVersionOfActivity( Activity inActivity ); + + // Returns the sequence variant of a gesture activity + static Activity GetSequenceVersionOfGesture( Activity inActivity ); + + //----------------------------------------------------- + + virtual bool ShouldPlayFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity ); + virtual Activity SelectFakeSequenceGesture( Activity nActivity, Activity nTranslatedActivity ); + void PlayFakeSequenceGesture( Activity nActivity, Activity nSequence, Activity nTranslatedSequence ); + + int GetFakeSequenceGesture(); + void ResetFakeSequenceGesture(); +#endif + private: void AdvanceToIdealActivity(void); @@ -1033,6 +1053,10 @@ private: Activity m_IdealTranslatedActivity; // Desired actual translated animation state Activity m_IdealWeaponActivity; // Desired weapon animation state +#ifdef MAPBASE + int m_FakeSequenceGestureLayer; // The gesture layer impersonating a sequence (-1 if invalid) +#endif + CNetworkVar(int, m_iDeathPose ); CNetworkVar(int, m_iDeathFrame ); @@ -1217,6 +1241,8 @@ public: #endif #ifdef MAPBASE_VSCRIPT +private: + // VScript stuff uses "VScript" instead of just "Script" to avoid // confusion with NPC_STATE_SCRIPT or StartScripting HSCRIPT VScriptGetEnemy(); @@ -1243,6 +1269,11 @@ public: int ScriptTranslateActivity( const char *szActivity ) { return TranslateActivity( (Activity)GetActivityID( szActivity ) ); } int ScriptTranslateActivityID( int iActivity ) { return TranslateActivity( (Activity)iActivity ); } + const char* VScriptGetGestureVersionOfActivity( const char *pszActivity ) { return GetActivityName( GetGestureVersionOfActivity( (Activity)GetActivityID( pszActivity ) ) ); } + int VScriptGetGestureVersionOfActivityID( int iActivity ) { return GetGestureVersionOfActivity( (Activity)iActivity ); } + const char* VScriptGetSequenceVersionOfGesture( const char *pszActivity ) { return GetActivityName( GetSequenceVersionOfGesture( (Activity)GetActivityID( pszActivity ) ) ); } + int VScriptGetSequenceVersionOfGestureID( int iActivity ) { return GetSequenceVersionOfGesture( (Activity)iActivity ); } + const char* VScriptGetSchedule(); int VScriptGetScheduleID(); void VScriptSetSchedule( const char *szSchedule ); @@ -1731,8 +1762,8 @@ public: virtual bool DoHolster(void); virtual bool DoUnholster(void); - virtual bool ShouldUnholsterWeapon() { return GetState() == NPC_STATE_COMBAT; } - virtual bool CanUnholsterWeapon() { return IsWeaponHolstered(); } + virtual bool ShouldUnholsterWeapon(); + virtual bool CanUnholsterWeapon(); void InputGiveWeaponHolstered( inputdata_t &inputdata ); void InputChangeWeapon( inputdata_t &inputdata ); @@ -2207,6 +2238,10 @@ public: inline void ForceCrouch( void ); inline void ClearForceCrouch( void ); +#ifdef MAPBASE + bool CouldShootIfCrouchingAt( const Vector &vecPosition, const Vector &vecForward, const Vector &vecRight, float flDist = 48.0f ); +#endif + protected: virtual bool Crouch( void ); virtual bool Stand( void ); @@ -2266,6 +2301,16 @@ private: static CAI_GlobalScheduleNamespace gm_SchedulingSymbols; static CAI_ClassScheduleIdSpace gm_ClassScheduleIdSpace; +#ifdef MAPBASE + typedef struct + { + Activity sequence; + Activity gesture; + } actlink_t; + + static actlink_t gm_ActivityGestureLinks[]; +#endif + public: //---------------------------------------------------- // Debugging tools @@ -2317,6 +2362,7 @@ public: static ScriptHook_t g_Hook_TranslateSchedule; static ScriptHook_t g_Hook_GetActualShootPosition; static ScriptHook_t g_Hook_OverrideMove; + static ScriptHook_t g_Hook_ShouldPlayFakeSequenceGesture; #endif private: diff --git a/sp/src/game/server/ai_basenpc_schedule.cpp b/sp/src/game/server/ai_basenpc_schedule.cpp index cf425fd0..61fefb3f 100644 --- a/sp/src/game/server/ai_basenpc_schedule.cpp +++ b/sp/src/game/server/ai_basenpc_schedule.cpp @@ -980,11 +980,12 @@ bool CAI_BaseNPC::FindCoverFromEnemy( bool bNodesOnly, float flMinDistance, floa // FIXME: add to goal if (GetHintNode()) { - GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) ); #ifdef MAPBASE - if (GetHintNode()->GetIgnoreFacing() != HIF_NO) -#endif + GetHintNode()->NPCHandleStartNav( this, true ); +#else + GetNavigator()->SetArrivalActivity( GetCoverActivity( GetHintNode() ) ); GetNavigator()->SetArrivalDirection( GetHintNode()->GetDirection() ); +#endif } return true; @@ -1363,6 +1364,14 @@ void CAI_BaseNPC::StartTask( const Task_t *pTask ) break; case TASK_STOP_MOVING: +#ifdef MAPBASE + if ( GetNavType() == NAV_CLIMB ) + { + // Don't clear the goal so that the climb can finish + DbgNavMsg( this, "Start TASK_STOP_MOVING with climb workaround\n" ); + } + else +#endif if ( ( GetNavigator()->IsGoalSet() && GetNavigator()->IsGoalActive() ) || GetNavType() == NAV_JUMP ) { DbgNavMsg( this, "Start TASK_STOP_MOVING\n" ); @@ -3348,8 +3357,40 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) // a navigation while in the middle of a climb if (GetNavType() == NAV_CLIMB) { +#ifdef MAPBASE + if (GetActivity() != ACT_CLIMB_DISMOUNT) + { + // Try to just pause the climb, but dismount if we're in SCHED_FAIL + if (IsCurSchedule( SCHED_FAIL, false )) + { + GetMotor()->MoveClimbStop(); + } + else + { + GetMotor()->MoveClimbPause(); + } + + TaskComplete(); + } + else if (IsActivityFinished()) + { + // Dismount complete. + GetMotor()->MoveClimbStop(); + + // Fix up our position if we have to + Vector vecTeleportOrigin; + if (GetMotor()->MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin )) + { + SetLocalOrigin( vecTeleportOrigin ); + } + + TaskComplete(); + } + break; +#else // wait until you reach the end break; +#endif } DbgNavMsg( this, "TASK_STOP_MOVING Complete\n" ); @@ -3394,6 +3435,17 @@ void CAI_BaseNPC::RunTask( const Task_t *pTask ) // If the yaw is locked, this function will not act correctly Assert( GetMotor()->IsYawLocked() == false ); +#ifdef MAPBASE + if ( GetHintNode() && GetHintNode()->OverridesNPCYaw( this ) ) + { + // If the yaw is supposed to use that of a hint node, chain to TASK_FACE_HINTNODE + GetMotor()->SetIdealYaw( GetHintNode()->Yaw() ); + GetMotor()->SetIdealYaw( CalcReasonableFacing( true ) ); // CalcReasonableFacing() is based on previously set ideal yaw + ChainRunTask( TASK_FACE_HINTNODE, pTask->flTaskData ); + break; + } +#endif + Vector vecEnemyLKP = GetEnemyLKP(); if (!FInAimCone( vecEnemyLKP )) { @@ -4280,6 +4332,15 @@ void CAI_BaseNPC::SetTurnActivity ( void ) float flYD; flYD = GetMotor()->DeltaIdealYaw(); +#ifdef MAPBASE + // Allow AddTurnGesture() to decide this + if (GetMotor()->AddTurnGesture( flYD )) + { + SetIdealActivity( ACT_IDLE ); + Remember( bits_MEMORY_TURNING ); + return; + } +#else // FIXME: unknown case, update yaw should catch these /* if (GetMotor()->AddTurnGesture( flYD )) @@ -4289,6 +4350,7 @@ void CAI_BaseNPC::SetTurnActivity ( void ) return; } */ +#endif if( flYD <= -80 && flYD >= -100 && SelectWeightedSequence( ACT_90_RIGHT ) != ACTIVITY_NOT_AVAILABLE ) { diff --git a/sp/src/game/server/ai_behavior.cpp b/sp/src/game/server/ai_behavior.cpp index 8822f750..4730e122 100644 --- a/sp/src/game/server/ai_behavior.cpp +++ b/sp/src/game/server/ai_behavior.cpp @@ -420,6 +420,24 @@ bool CAI_BehaviorBase::CanUnholsterWeapon( void ) return m_pBackBridge->BackBridge_CanUnholsterWeapon(); } + +//------------------------------------- + +bool CAI_BehaviorBase::ShouldPickADeathPose( void ) +{ + Assert( m_pBackBridge != NULL ); + + return m_pBackBridge->BackBridge_ShouldPickADeathPose(); +} + +//------------------------------------- + +bool CAI_BehaviorBase::CanTranslateCrouchActivity( void ) +{ + Assert( m_pBackBridge != NULL ); + + return m_pBackBridge->BackBridge_CanTranslateCrouchActivity(); +} #endif //------------------------------------- diff --git a/sp/src/game/server/ai_behavior.h b/sp/src/game/server/ai_behavior.h index ce26ca61..f4df0640 100644 --- a/sp/src/game/server/ai_behavior.h +++ b/sp/src/game/server/ai_behavior.h @@ -132,6 +132,8 @@ public: void BridgeHandleAnimEvent( animevent_t *pEvent ); #ifdef MAPBASE bool BridgeCanUnholsterWeapon( void ); + bool BridgeShouldPickADeathPose( void ); + bool BridgeCanTranslateCrouchActivity( void ); #endif virtual void GatherConditions(); @@ -220,6 +222,8 @@ protected: virtual void HandleAnimEvent( animevent_t *pEvent ); #ifdef MAPBASE virtual bool CanUnholsterWeapon( void ); + virtual bool ShouldPickADeathPose( void ); + virtual bool CanTranslateCrouchActivity( void ); #endif virtual bool ShouldAlwaysThink(); @@ -370,6 +374,9 @@ public: #ifdef MAPBASE // For func_tank behavior virtual bool BackBridge_CanUnholsterWeapon( void ) = 0; + + virtual bool BackBridge_ShouldPickADeathPose( void ) = 0; + virtual bool BackBridge_CanTranslateCrouchActivity( void ) = 0; #endif //------------------------------------- @@ -470,6 +477,8 @@ public: void HandleAnimEvent( animevent_t *pEvent ); #ifdef MAPBASE bool CanUnholsterWeapon( void ); + bool ShouldPickADeathPose( void ); + bool CanTranslateCrouchActivity( void ); #endif bool ShouldAlwaysThink(); @@ -534,6 +543,9 @@ private: #ifdef MAPBASE // For func_tank behavior bool BackBridge_CanUnholsterWeapon( void ); + + bool BackBridge_ShouldPickADeathPose( void ); + bool BackBridge_CanTranslateCrouchActivity( void ); #endif CAI_BehaviorBase **AccessBehaviors(); @@ -913,6 +925,20 @@ inline bool CAI_BehaviorBase::BridgeCanUnholsterWeapon( void ) { return CanUnholsterWeapon(); } + +//----------------------------------------------------------------------------- + +inline bool CAI_BehaviorBase::BridgeShouldPickADeathPose( void ) +{ + return ShouldPickADeathPose(); +} + +//----------------------------------------------------------------------------- + +inline bool CAI_BehaviorBase::BridgeCanTranslateCrouchActivity( void ) +{ + return CanTranslateCrouchActivity(); +} #endif //----------------------------------------------------------------------------- @@ -1498,6 +1524,22 @@ inline bool CAI_BehaviorHost::BackBridge_CanUnholsterWeapon( void ) { return BaseClass::CanUnholsterWeapon(); } + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::BackBridge_ShouldPickADeathPose( void ) +{ + return BaseClass::ShouldPickADeathPose(); +} + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::BackBridge_CanTranslateCrouchActivity( void ) +{ + return BaseClass::CanTranslateCrouchActivity(); +} #endif //------------------------------------- @@ -1914,6 +1956,28 @@ inline bool CAI_BehaviorHost::CanUnholsterWeapon( void ) return BaseClass::CanUnholsterWeapon(); } + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::ShouldPickADeathPose( void ) +{ + if (m_pCurBehavior) + return m_pCurBehavior->BridgeShouldPickADeathPose(); + + return BaseClass::ShouldPickADeathPose(); +} + +//------------------------------------- + +template +inline bool CAI_BehaviorHost::CanTranslateCrouchActivity( void ) +{ + if (m_pCurBehavior) + return m_pCurBehavior->BridgeCanTranslateCrouchActivity(); + + return BaseClass::CanTranslateCrouchActivity(); +} #endif //------------------------------------- diff --git a/sp/src/game/server/ai_behavior_standoff.cpp b/sp/src/game/server/ai_behavior_standoff.cpp index bc5244ef..85740876 100644 --- a/sp/src/game/server/ai_behavior_standoff.cpp +++ b/sp/src/game/server/ai_behavior_standoff.cpp @@ -535,10 +535,11 @@ int CAI_StandoffBehavior::SelectScheduleCheckCover( void ) { StandoffMsg( "Regulated to not shoot\n" ); #ifdef MAPBASE - if ( GetHintType() == HINT_TACTICAL_COVER_LOW || GetHintType() == HINT_TACTICAL_COVER_MED ) -#else - if ( GetHintType() == HINT_TACTICAL_COVER_LOW ) + if ( GetHintType() == HINT_TACTICAL_COVER_MED || GetCoverActivity() == ACT_COVER_MED ) + SetPosture( AIP_CROUCHING_MED ); + else #endif + if ( GetHintType() == HINT_TACTICAL_COVER_LOW ) SetPosture( AIP_CROUCHING ); else SetPosture( AIP_STANDING ); @@ -557,7 +558,11 @@ int CAI_StandoffBehavior::SelectScheduleEstablishAim( void ) { if ( HasCondition( COND_ENEMY_OCCLUDED ) ) { +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + if ( GetPosture() == AIP_CROUCHING || GetPosture() == AIP_CROUCHING_MED ) +#else if ( GetPosture() == AIP_CROUCHING ) +#endif { // force a stand up, just in case GetOuter()->SpeakSentence( STANDOFF_SENTENCE_STAND_CHECK_TARGET ); @@ -668,6 +673,15 @@ Activity CAI_MappedActivityBehavior_Temporary::GetMappedActivity( AI_Posture_t p { if ( posture != AIP_STANDING ) { +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + // See UpdateTranslateActivityMap() for more information on what this is for + if ( posture == AIP_CROUCHING_MED ) + { + if (activity != ACT_RANGE_ATTACK1) + posture = AIP_CROUCHING; + } +#endif + unsigned short iActivityTranslation = m_ActivityMap.Find( MAKE_ACTMAP_KEY( posture, activity ) ); if ( iActivityTranslation != m_ActivityMap.InvalidIndex() ) { @@ -685,10 +699,28 @@ Activity CAI_StandoffBehavior::NPC_TranslateActivity( Activity activity ) Activity coverActivity = GetCoverActivity(); if ( coverActivity != ACT_INVALID ) { +#ifdef MAPBASE + if ( GetPosture() == AIP_STANDING ) + { + if ( coverActivity == ACT_COVER_LOW ) + SetPosture( AIP_CROUCHING ); + else if ( coverActivity == ACT_COVER_MED ) + { + SetPosture( AIP_CROUCHING_MED ); + coverActivity = ACT_COVER_LOW; + } + } + else if (coverActivity == ACT_COVER_MED) + coverActivity = ACT_COVER_LOW; + if ( activity == ACT_IDLE ) activity = coverActivity; +#else + if (activity == ACT_IDLE) + activity = coverActivity; if ( GetPosture() == AIP_STANDING && coverActivity == ACT_COVER_LOW ) SetPosture( AIP_CROUCHING ); +#endif } Activity result = GetMappedActivity( GetPosture(), activity ); @@ -1089,12 +1121,25 @@ void CAI_StandoffBehavior::UnlockHintNode() Activity CAI_StandoffBehavior::GetCoverActivity() { -#ifdef MAPBASE - // This does two things: - // A. Allows medium cover nodes to be used, kind of. - // B. GetCoverActivity() already checks everything we checked here. - Activity coveract = GetOuter()->GetCoverActivity( GetHintNode() ); - return coveract == ACT_IDLE ? ACT_INVALID : coveract; +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + // GetCoverActivity() already checks everything we checked here. + Activity coverActivity = GetOuter()->GetCoverActivity( GetHintNode() ); + + if (coverActivity == ACT_COVER_LOW) + { + // Check if this node will block our line of sight if aiming low. + Vector vHintPos, vHintForward, vHintRight; + GetHintNode()->GetPosition( GetHullType(), &vHintPos ); + vHintForward = GetHintNode()->GetDirection(); + + GetHintNode()->GetVectors( NULL, &vHintRight, NULL ); + if (GetOuter()->CouldShootIfCrouchingAt( vHintPos, vHintForward, vHintRight )) + { + coverActivity = ACT_COVER_MED; + } + } + + return coverActivity == ACT_IDLE ? ACT_INVALID : coverActivity; #else CAI_Hint *pHintNode = GetHintNode(); if ( pHintNode && pHintNode->HintType() == HINT_TACTICAL_COVER_LOW ) @@ -1112,6 +1157,14 @@ struct AI_ActivityMapping_t Activity activity; const char * pszWeapon; Activity translation; +#ifdef MAPBASE + Activity backup; + + AI_ActivityMapping_t( AI_Posture_t _p, Activity _a, const char *_w, Activity _t, Activity _b = ACT_INVALID ) + { + posture = _p; activity = _a; pszWeapon = _w; translation = _t; backup = _b; + } +#endif }; void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() @@ -1125,15 +1178,60 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() { AIP_CROUCHING, ACT_WALK_AIM, NULL, ACT_WALK_CROUCH_AIM, }, { AIP_CROUCHING, ACT_RUN_AIM, NULL, ACT_RUN_CROUCH_AIM, }, { AIP_CROUCHING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, }, +#ifdef MAPBASE + { AIP_CROUCHING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_LOW, }, + { AIP_CROUCHING, ACT_COVER_MED, NULL, ACT_COVER_LOW, }, +#else { AIP_CROUCHING, ACT_RANGE_ATTACK_SMG1, NULL, ACT_RANGE_ATTACK_SMG1_LOW, }, { AIP_CROUCHING, ACT_RANGE_ATTACK_AR2, NULL, ACT_RANGE_ATTACK_AR2_LOW, }, +#endif +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + // + // ============ Really long explanation that should be in a wiki/documentation article somewhere ~ Blixibon, 10/27/2021 ============ + // + // Standoff behavior assumes low attack animations allow NPCs to see over barricades, with ACT_COVER_LOW being their "safely in cover" animation. + // This is why AIP_CROUCHING translates ACT_RANGE_ATTACK1 to its low animation, but translates ACT_IDLE_ANGRY to ACT_COVER_LOW instead of ACT_RANGE_AIM_LOW, + // as this would ideally allow NPCs to pop in and out of cover to shoot. + // This is also why AIP_PEEKING translates ACT_COVER_LOW to ACT_RANGE_AIM_LOW, as it's supposed to force the NPC to peek over their cover. + // + // However, this assumption mainly just applies to metrocops. Citizens' low attacking animations crouch low to the ground (which isn't effective for + // shooting over most barricades) and, while they do have a distinct ACT_COVER_LOW animation with transitions, they are close enough together that popping + // in and out of cover is redundant in most cases. Meanwhile, Combine soldiers have identical ACT_COVER_LOW and ACT_RANGE_AIM_LOW animations, which means + // they do not pop in and out of cover and AIP_PEEKING does nothing. This may be the reason why Combine soldiers occasionally get stuck in cover after a fight. + // + // ------------------------------------------------------------- + // + // As part of Mapbase v7.0's NPC activity overhaul, a new "medium cover" activity set has been added. Metrocops' previous "low cover" animation set (which, as + // mentioned before, is different from that of other NPCs) has been retroactively changed to use "medium cover". This was done for a few reasons unrelated to + // standoff behavior, but the important point is that these activities indicate a new cover height. This means we can use them to give standoff behavior more leeway + // for judging which animations to use in various levels of cover. + // + // Standoff behavior can use "medium cover" animations in cover which is too high for the "low" animations, and when the medium cover animations are not available, + // it simply falls back to the "standing" animations, thus resolving the issue with other NPCs not peeking in and out of cover without requiring new medium cover + // animations. + // + // In Mapbase, this is done by changing AIP_PEEKING to use the medium cover animations and adding a new alternate crouching posture posture called "AIP_CROUCHING_MED", + // which only uses the medium cover attack activity and otherwise automatically falls back to AIP_CROUCHING. AIP_CROUCHING_MED is automatically set if the NPC cannot + // get LOS from a regular crouching position. + // + { AIP_CROUCHING_MED, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_MED, }, + + //---- + { AIP_PEEKING, ACT_IDLE, NULL, ACT_RANGE_AIM_MED, }, + { AIP_PEEKING, ACT_IDLE_ANGRY, NULL, ACT_RANGE_AIM_MED, }, + { AIP_PEEKING, ACT_COVER_LOW, NULL, ACT_RANGE_AIM_MED, ACT_IDLE_ANGRY }, + { AIP_PEEKING, ACT_COVER_MED, NULL, ACT_RANGE_AIM_MED, ACT_IDLE_ANGRY }, + { AIP_PEEKING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_MED, }, + { AIP_PEEKING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, }, +#else //---- { AIP_PEEKING, ACT_IDLE, NULL, ACT_RANGE_AIM_LOW, }, { AIP_PEEKING, ACT_IDLE_ANGRY, NULL, ACT_RANGE_AIM_LOW, }, { AIP_PEEKING, ACT_COVER_LOW, NULL, ACT_RANGE_AIM_LOW, }, { AIP_PEEKING, ACT_RANGE_ATTACK1, NULL, ACT_RANGE_ATTACK1_LOW, }, { AIP_PEEKING, ACT_RELOAD, NULL, ACT_RELOAD_LOW, }, +#endif }; m_ActivityMap.RemoveAll(); @@ -1145,7 +1243,7 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() if ( !mappings[i].pszWeapon || stricmp( mappings[i].pszWeapon, pszWeaponClass ) == 0 ) { #ifdef MAPBASE - // Check backup activity + // Check NPC backup activity if ( HaveSequenceForActivity( mappings[i].translation ) || HaveSequenceForActivity( GetOuter()->Weapon_TranslateActivity( mappings[i].translation ) ) || HaveSequenceForActivity( GetOuter()->Weapon_BackupActivity( mappings[i].translation ) ) ) #else if ( HaveSequenceForActivity( mappings[i].translation ) || HaveSequenceForActivity( GetOuter()->Weapon_TranslateActivity( mappings[i].translation ) ) ) @@ -1154,6 +1252,14 @@ void CAI_MappedActivityBehavior_Temporary::UpdateTranslateActivityMap() Assert( m_ActivityMap.Find( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ) ) == m_ActivityMap.InvalidIndex() ); m_ActivityMap.Insert( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ), mappings[i].translation ); } +#ifdef MAPBASE + // Check activity map backup activity + else if ( mappings[i].backup != ACT_INVALID && HaveSequenceForActivity( mappings[i].backup ) ) + { + Assert( m_ActivityMap.Find( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ) ) == m_ActivityMap.InvalidIndex() ); + m_ActivityMap.Insert( MAKE_ACTMAP_KEY( mappings[i].posture, mappings[i].activity ), mappings[i].backup ); + } +#endif } } } diff --git a/sp/src/game/server/ai_behavior_standoff.h b/sp/src/game/server/ai_behavior_standoff.h index c08059e8..2de2a072 100644 --- a/sp/src/game/server/ai_behavior_standoff.h +++ b/sp/src/game/server/ai_behavior_standoff.h @@ -51,6 +51,9 @@ enum AI_Posture_t AIP_INDIFFERENT, AIP_STANDING, AIP_CROUCHING, +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + AIP_CROUCHING_MED, // See UpdateTranslateActivityMap() for more information on what this is for +#endif AIP_PEEKING, }; @@ -149,6 +152,14 @@ protected: // Standoff overrides base AI crouch handling bool IsCrouching( void ) { return false; } + +#ifdef MAPBASE + // Standoff overrides base cover activity translation + bool CanTranslateCrouchActivity( void ) { return false; } + + // Don't do death poses while crouching + bool ShouldPickADeathPose( void ) { return (GetPosture() != AIP_CROUCHING && GetPosture() != AIP_PEEKING) && BaseClass::ShouldPickADeathPose(); } +#endif private: diff --git a/sp/src/game/server/ai_blended_movement.cpp b/sp/src/game/server/ai_blended_movement.cpp index d1b32d14..0a11c915 100644 --- a/sp/src/game/server/ai_blended_movement.cpp +++ b/sp/src/game/server/ai_blended_movement.cpp @@ -1640,10 +1640,17 @@ void CAI_BlendedMotor::MaintainTurnActivity( void ) ConVar scene_flatturn( "scene_flatturn", "1" ); +#ifdef MAPBASE +ConVar ai_turning_enabled( "ai_turning_enabled", "1", FCVAR_NONE, "Enables NPC turning, which was previously disabled by Valve at some point after 2004 due to a now-unknown major issue." ); +#endif + bool CAI_BlendedMotor::AddTurnGesture( float flYD ) { // some funky bug with human turn gestures, disable for now +#ifdef MAPBASE + if (!ai_turning_enabled.GetBool()) +#endif return false; // try using a turn gesture diff --git a/sp/src/game/server/ai_hint.cpp b/sp/src/game/server/ai_hint.cpp index 1f8ec4d1..2e8e90af 100644 --- a/sp/src/game/server/ai_hint.cpp +++ b/sp/src/game/server/ai_hint.cpp @@ -35,6 +35,10 @@ CHintCriteria::CHintCriteria( void ) m_strGroup = NULL_STRING; m_iFlags = 0; m_HintTypes.Purge(); +#ifdef MAPBASE // From Alien Swarm SDK + m_pfnFilter = NULL; + m_pFilterContext = NULL; +#endif } //----------------------------------------------------------------------------- @@ -1117,10 +1121,10 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing ) HintIgnoreFacing_t facing = GetIgnoreFacing(); if (facing == HIF_DEFAULT) - facing = bDefaultFacing ? HIF_YES : HIF_NO; + facing = bDefaultFacing ? HIF_NO : HIF_YES; - if (facing == HIF_YES) - pNPC->GetNavigator()->SetArrivalDirection(GetDirection()); + if (facing == HIF_NO) + pNPC->GetNavigator()->SetArrivalDirection( GetDirection() ); if (HintActivityName() != NULL_STRING) { @@ -1139,6 +1143,51 @@ void CAI_Hint::NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing ) } } } + +//----------------------------------------------------------------------------- +// Purpose: Returns true if this hint should override a NPC's yaw even during regular AI. +//----------------------------------------------------------------------------- +bool CAI_Hint::OverridesNPCYaw( CAI_BaseNPC *pNPC ) +{ + switch (HintType()) + { + case HINT_TACTICAL_COVER_CUSTOM: + case HINT_TACTICAL_COVER_MED: + case HINT_TACTICAL_COVER_LOW: + { + if (pNPC->HasMemory( bits_MEMORY_INCOVER )) + { + // By default, don't override yaw on cover nodes unless they use custom activities. + HintIgnoreFacing_t facing = GetIgnoreFacing(); + if (facing == HIF_DEFAULT) + return ( HintActivityName() != NULL_STRING ); + + return facing == HIF_NO; + } + + break; + } + + case HINT_PLAYER_ALLY_MOVE_AWAY_DEST: + { + Vector vHintPos; + GetPosition( pNPC, &vHintPos ); + if (VectorsAreEqual( vHintPos, pNPC->GetAbsOrigin(), 0.1f )) + { + // By default, don't override yaw on move away destinations unless they use custom activities. + HintIgnoreFacing_t facing = GetIgnoreFacing(); + if (facing == HIF_DEFAULT) + return ( HintActivityName() != NULL_STRING ); + + return facing == HIF_NO; + } + + break; + } + } + + return false; +} #endif //----------------------------------------------------------------------------- @@ -1258,6 +1307,30 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint return false; } +#ifdef MAPBASE + // Test against generic filter + // (From Alien Swarm SDK) + if ( !hintCriteria.PassesFilter( this ) ) + { + REPORTFAILURE( "Failed filter test" ); + return false; + } + + // (From Alien Swarm SDK) + int nRadius = GetRadius(); + if ( nRadius != 0 ) + { + // Calculate our distance + float distance = (GetAbsOrigin() - position).LengthSqr(); + + if ( distance > nRadius * nRadius ) + { + REPORTFAILURE( "NPC is not within the node's radius." ); + return false; + } + } +#endif + if ( hintCriteria.HasFlag(bits_HINT_NPC_IN_NODE_FOV) ) { if ( pNPC == NULL ) @@ -1377,10 +1450,18 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint { trace_t tr; // Can my bounding box fit there? +#ifdef MAPBASE // From Alien Swarm SDK + Vector vStep( 0, 0, pNPC->StepHeight() ); + AI_TraceHull ( GetAbsOrigin() + vStep, GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs() - vStep, + MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr ); + + if ( tr.fraction < 0.95 ) +#else AI_TraceHull ( GetAbsOrigin(), GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs(), MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction != 1.0 ) +#endif { REPORTFAILURE( "Node isn't clear." ); return false; @@ -1396,6 +1477,15 @@ bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hint // Calculate our distance float distance = (GetAbsOrigin() - position).Length(); +#ifdef MAPBASE + // Divide by hint weight + float flWeight = GetHintWeight(); + if ( flWeight != 1.0f ) + { + distance *= GetHintWeightInverse(); + } +#endif + // Must be closer than the current best if ( distance > *flNearestDistance ) { @@ -1576,6 +1666,14 @@ void CAI_Hint::OnRestore() m_NodeData.nNodeID = g_pAINetworkManager->GetEditOps()->GetNodeIdFromWCId( m_NodeData.nWCNodeID ); FixupTargetNode(); +#ifdef MAPBASE + if (m_NodeData.flWeight != 0.0f && m_NodeData.flWeight != 1.0f) + { + // Re-invert the weight + m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight; + } +#endif + CAI_Node *pNode = GetNode(); if ( !pNode ) @@ -1751,6 +1849,11 @@ void CC_ai_drop_hint( const CCommand &args ) nodeData.fIgnoreFacing = HIF_DEFAULT; nodeData.minState = NPC_STATE_IDLE; nodeData.maxState = NPC_STATE_COMBAT; +#ifdef MAPBASE + nodeData.nRadius = 0; // From Alien Swarm SDK + nodeData.flWeight = 1.0f; + nodeData.flWeightInverse = 1.0f; +#endif CAI_Hint *pHint = CAI_HintManager::CreateHint( &nodeData, NULL ); if ( pHint ) { diff --git a/sp/src/game/server/ai_hint.h b/sp/src/game/server/ai_hint.h index 60052ded..570f85ef 100644 --- a/sp/src/game/server/ai_hint.h +++ b/sp/src/game/server/ai_hint.h @@ -112,6 +112,13 @@ enum Hint_e // CS port hints HINT_CSTRIKE_HOSTAGE_ESCAPE = 1100, + +#ifdef MAPBASE + // Mapbase hints + // (these start at a high number to avoid potential conflicts with mod hints) + + HINT_TACTICAL_COVER_CUSTOM = 10000, // Cover node with a custom hint activity (NPCs can take cover and reload here while playing said activity) +#endif }; const char *GetHintTypeDescription( Hint_e iHintType ); const char *GetHintTypeDescription( CAI_Hint *pHint ); @@ -120,6 +127,10 @@ const char *GetHintTypeDescription( CAI_Hint *pHint ); // CHintCriteria //----------------------------------------------------------------------------- +#ifdef MAPBASE // From Alien Swarm SDK +typedef bool (*HintSearchFilterFunc_t)( void *pContext, CAI_Hint *pCandidate ); +#endif + class CHintCriteria { public: @@ -134,6 +145,11 @@ public: void SetGroup( string_t group ); string_t GetGroup( void ) const { return m_strGroup; } +#ifdef MAPBASE // From Alien Swarm SDK + void SetFilterFunc( HintSearchFilterFunc_t pfnFilter, void *pContext = NULL ) { m_pfnFilter = pfnFilter; m_pFilterContext = pContext; } + bool PassesFilter( CAI_Hint *pCandidate ) const { return (m_pfnFilter) ? (*m_pfnFilter)(m_pFilterContext, pCandidate) : true; } +#endif + int GetFirstHintType( void ) const { return m_iFirstHintType; } int GetLastHintType( void ) const { return m_iLastHintType; } bool MatchesHintType( int hintType ) const; @@ -176,6 +192,11 @@ private: zoneList_t m_zoneInclude; zoneList_t m_zoneExclude; + +#ifdef MAPBASE + HintSearchFilterFunc_t m_pfnFilter; + void * m_pFilterContext; +#endif }; class CAI_Node; @@ -310,11 +331,21 @@ public: int GetNodeId() { return m_NodeData.nNodeID; } int GetWCId() { return m_NodeData.nWCNodeID; } +#ifdef MAPBASE + int GetRadius() const { return m_NodeData.nRadius; } // From Alien Swarm SDK + + float GetHintWeight() const { return m_NodeData.flWeight; } + float GetHintWeightInverse() const { return m_NodeData.flWeightInverse; } // Used to multiply distances +#endif + bool HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria, const Vector &position, float *flNearestDistance, bool bIgnoreLock = false, bool bIgnoreHintType = false ); bool IsInNodeFOV( CBaseEntity *pOther ); #ifdef MAPBASE void NPCHandleStartNav( CAI_BaseNPC *pNPC, bool bDefaultFacing ); + + // Returns true if this hint should override a NPC's yaw even during regular AI. + bool OverridesNPCYaw( CAI_BaseNPC *pNPC ); #endif #ifdef MAPBASE_VSCRIPT diff --git a/sp/src/game/server/ai_initutils.cpp b/sp/src/game/server/ai_initutils.cpp index 62d5c6ec..cd792798 100644 --- a/sp/src/game/server/ai_initutils.cpp +++ b/sp/src/game/server/ai_initutils.cpp @@ -173,6 +173,10 @@ BEGIN_SIMPLE_DATADESC( HintNodeData ) DEFINE_KEYFIELD( fIgnoreFacing, FIELD_INTEGER, "IgnoreFacing" ), DEFINE_KEYFIELD( minState, FIELD_INTEGER, "MinimumState" ), DEFINE_KEYFIELD( maxState, FIELD_INTEGER, "MaximumState" ), +#ifdef MAPBASE + DEFINE_KEYFIELD( nRadius, FIELD_INTEGER, "radius" ), // From Alien Swarm SDK + DEFINE_KEYFIELD( flWeight, FIELD_FLOAT, "hintweight" ), +#endif END_DATADESC() @@ -205,6 +209,17 @@ int CNodeEnt::Spawn( const char *pMapData ) m_NodeData.minState = NPC_STATE_IDLE; if ( m_NodeData.maxState == NPC_STATE_NONE ) m_NodeData.maxState = NPC_STATE_COMBAT; +#ifdef MAPBASE + if (m_NodeData.flWeight == 0.0f) + { + m_NodeData.flWeight = 1.0f; + } + else if (m_NodeData.flWeight != 1.0f) + { + // Invert the weight so that it could be used as a direct multiplier for distances, etc. + m_NodeData.flWeightInverse = 1.0f / m_NodeData.flWeight; + } +#endif // --------------------------------------------------------------------------------- // If just a hint node (not used for navigation) just create a hint and bail // --------------------------------------------------------------------------------- diff --git a/sp/src/game/server/ai_initutils.h b/sp/src/game/server/ai_initutils.h index 22aaf5f3..70bd019c 100644 --- a/sp/src/game/server/ai_initutils.h +++ b/sp/src/game/server/ai_initutils.h @@ -42,6 +42,11 @@ struct HintNodeData HintIgnoreFacing_t fIgnoreFacing; NPC_STATE minState; NPC_STATE maxState; +#ifdef MAPBASE + int nRadius; // From Alien Swarm SDK + float flWeight; + float flWeightInverse; // Not saved +#endif int nWCNodeID; // Node ID assigned by worldcraft (not same as engine!) diff --git a/sp/src/game/server/ai_motor.cpp b/sp/src/game/server/ai_motor.cpp index 49f3d5f4..f54432ff 100644 --- a/sp/src/game/server/ai_motor.cpp +++ b/sp/src/game/server/ai_motor.cpp @@ -14,6 +14,9 @@ #include "ai_basenpc.h" #include "ai_localnavigator.h" #include "ai_moveprobe.h" +#ifdef MAPBASE +#include "ai_hint.h" +#endif #include "saverestore_utlvector.h" // memdbgon must be the last include file in a .cpp file!!! @@ -235,18 +238,47 @@ void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir // > code are not reciprocal for all state, and furthermore, stomp // > other state? + bool bGoingUp = (climbDir.z > 0.01); +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + if ( bGoingUp && GetOuter()->HaveSequenceForActivity( ACT_CLIMB_MOUNT_BOTTOM ) ) + { + SetActivity( ACT_CLIMB_MOUNT_BOTTOM ); + + // Steal m_vecDismount for this + GetOuter()->GetSequenceLinearMotion( GetSequence(), &m_vecDismount ); + GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) ); + } + else if ( !bGoingUp && GetOuter()->HaveSequenceForActivity( ACT_CLIMB_MOUNT_TOP ) ) + { + SetActivity( ACT_CLIMB_MOUNT_TOP ); + + // Steal m_vecDismount for this + GetOuter()->GetSequenceLinearMotion( GetSequence(), &m_vecDismount ); + GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) ); + } + else +#endif if ( fabsf( climbDir.z ) < .1 ) { SetActivity( GetNavigator()->GetMovementActivity() ); } else { - SetActivity( (climbDir.z > -0.01 ) ? ACT_CLIMB_UP : ACT_CLIMB_DOWN ); + SetActivity( bGoingUp ? ACT_CLIMB_UP : ACT_CLIMB_DOWN ); } m_nDismountSequence = SelectWeightedSequence( ACT_CLIMB_DISMOUNT ); if (m_nDismountSequence != ACT_INVALID) { +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + if ( !bGoingUp ) + { + int nBottomDismount = SelectWeightedSequence( ACT_CLIMB_DISMOUNT_BOTTOM ); + if (nBottomDismount != ACTIVITY_NOT_AVAILABLE) + m_nDismountSequence = nBottomDismount; + } +#endif + GetOuter()->GetSequenceLinearMotion( m_nDismountSequence, &m_vecDismount ); } else @@ -262,6 +294,76 @@ void CAI_Motor::MoveClimbStart( const Vector &climbDest, const Vector &climbDir AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft ) { +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + if ( (GetActivity() == ACT_CLIMB_MOUNT_TOP || GetActivity() == ACT_CLIMB_MOUNT_BOTTOM) ) + { + if (!GetOuter()->IsActivityFinished()) + { + // Wait for the mounting to finish + SetGroundEntity( NULL ); + } + else + { + // Fix up our position if we have to + Vector vecTeleportOrigin; + if (MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin )) + { + SetLocalOrigin( vecTeleportOrigin ); + } + + // Reset activity and start from the beginning + GetOuter()->ResetActivity(); + return MoveClimbExecute( climbDest, climbDir, climbDist, yaw, climbNodesLeft ); + } + } + else if ( fabsf( climbDir.z ) > .1 && (GetActivity() != ACT_CLIMB_DISMOUNT && GetActivity() != ACT_CLIMB_DISMOUNT_BOTTOM) ) + { + bool bGoingUp = (climbDir.z > -0.01); + if ( GetOuter()->HaveSequenceForActivity( ACT_CLIMB_ALL ) ) + { + SetActivity( ACT_CLIMB_ALL ); + + // TODO: Use UTIL_VecToPitch() instead if move_yaw becomes a true climb yaw and not just an up-down scalar + SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), climbDir.z < 0 ? 180.0 : -180.0 ); + } + else + { + Activity desiredActivity = bGoingUp ? ACT_CLIMB_UP : ACT_CLIMB_DOWN; + if ( GetActivity() != desiredActivity ) + { + SetActivity( desiredActivity ); + } + } + + if (m_nDismountSequence != ACT_INVALID) + { + if (climbNodesLeft <= 2 && climbDist < fabs( m_vecDismount.z )) + { + if (bGoingUp) + { + // fixme: No other way to force m_nIdealSequence? + GetOuter()->SetActivity( ACT_CLIMB_DISMOUNT ); + GetOuter()->SetCycle( GetOuter()->GetMovementFrame( m_vecDismount.z - climbDist ) ); + } + else + { + if (GetSequence() != m_nDismountSequence && GetOuter()->GetSequenceActivity( m_nDismountSequence ) == ACT_CLIMB_DISMOUNT_BOTTOM) + { + SetActivity( ACT_CLIMB_DISMOUNT_BOTTOM ); + } + } + } + } + } + else if ( climbDir.Length() == 0 && GetOuter()->GetInstantaneousVelocity() <= 0.01 ) + { + // The NPC is somehow stuck climbing with no direction or movement. + // This can be caused by NPCs getting stuck in each other and/or being moved away from the ladder. + // In these cases, the NPC has to be made unstuck, or else they may remain in an immobile climbing state forever. + Warning( "%s had to abort climbing due to no direction or movement\n", GetOuter()->GetDebugName() ); + return AIMR_ILLEGAL; + } +#else if ( fabsf( climbDir.z ) > .1 ) { if ( GetActivity() != ACT_CLIMB_DISMOUNT ) @@ -292,13 +394,34 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto } } } +#endif float climbSpeed = GetOuter()->GetInstantaneousVelocity(); if (m_nDismountSequence != ACT_INVALID) { // catch situations where the climb mount/dismount finished before reaching goal +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + if ((GetActivity() == ACT_CLIMB_DISMOUNT || GetActivity() == ACT_CLIMB_DISMOUNT_BOTTOM)) + { + SetGroundEntity( NULL ); + + if (GetOuter()->IsActivityFinished()) + { + // Fix up our position if we have to + Vector vecTeleportOrigin; + if (MoveClimbShouldTeleportToSequenceEnd( vecTeleportOrigin )) + { + // Just force it to complete + climbDist = 0.0f; + } + + climbSpeed = 200.0f; + } + } +#else climbSpeed = MAX( climbSpeed, 30.0 ); +#endif } else { @@ -314,7 +437,7 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto climbDist = 0; const float climbTime = climbDist / climbSpeed; - + SetMoveInterval( GetMoveInterval() - climbTime ); SetLocalOrigin( climbDest ); @@ -330,6 +453,20 @@ AIMoveResult_t CAI_Motor::MoveClimbExecute( const Vector &climbDest, const Vecto // -------------------------------------------- SetIdealYawAndUpdate( yaw ); +#ifdef MAPBASE + // Lock the yaw if we're in position + if ( UTIL_AngleMod( yaw ) == UTIL_AngleMod( GetLocalAngles().y ) ) + { + SetYawLocked( true ); + } + else if ( IsYawLocked() ) + { + // We're in a different position now. Unlock the yaw and update it + SetYawLocked( false ); + UpdateYaw( -1 ); + } +#endif + return AIMR_OK; } @@ -340,11 +477,62 @@ void CAI_Motor::MoveClimbStop() else SetActivity( ACT_IDLE ); +#ifdef MAPBASE + // Unlock desired weapon state so NPCs can unholster their weapons again. + GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_IGNORE ); + + // Unlock yaw + SetYawLocked( false ); +#endif + GetOuter()->RemoveFlag( FL_FLY ); SetSmoothedVelocity( vec3_origin ); SetGravity( 1.0 ); } +#ifdef MAPBASE +void CAI_Motor::MoveClimbPause() +{ + if (GetActivity() != ACT_CLIMB_DISMOUNT +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + && GetActivity() != ACT_CLIMB_MOUNT_TOP && GetActivity() != ACT_CLIMB_MOUNT_BOTTOM +#endif + ) + { + if ( GetActivity() == ACT_CLIMB_ALL ) + { + SetPoseParameter( GetOuter()->LookupPoseMoveYaw(), 0.0f ); + } + + SetSmoothedVelocity( vec3_origin ); + } + else + { + // If already dismounting, do nothing + } +} + +//----------------------------------------------------------------------------- +// Purpose: This is part of a hack needed in cases where ladder mount/dismount animations collide with the world and don't move properly. +// It's based off of the same code as scripted_sequence's teleportation fixup, although this function only resolves the bone origin and +// returns whether or not teleportation is necessary, as the teleportation is achieved in different ways for different uses of this code. +//----------------------------------------------------------------------------- +bool CAI_Motor::MoveClimbShouldTeleportToSequenceEnd( Vector &teleportOrigin ) +{ + QAngle new_angle; + GetOuter()->GetBonePosition( 0, teleportOrigin, new_angle ); + + // Ensure that there is actually a distance needed to teleport there + if ((GetLocalOrigin() - teleportOrigin).Length2DSqr() > Square( 8.0 )) + { + teleportOrigin.z = GetLocalOrigin().z; + return true; + } + + return false; +} +#endif + //----------------------------------------------------------------------------- // Purpose: Motion for jumping // Input : @@ -651,9 +839,20 @@ void CAI_Motor::MoveFacing( const AILocalMoveGoal_t &move ) { // FIXME: move this up to navigator so that path goals can ignore these overrides. Vector dir; - float flInfluence = GetFacingDirection( dir ); - dir = move.facing * (1 - flInfluence) + dir * flInfluence; - VectorNormalize( dir ); + +#ifdef MAPBASE + if (IsDeceleratingToGoal() && (GetOuter()->GetHintNode() /*|| GetOuter()->m_hOpeningDoor*/)) + { + // Don't let the facing queue interfere with arrival direction in important cases + dir = move.facing; + } + else +#endif + { + float flInfluence = GetFacingDirection( dir ); + dir = move.facing * (1 - flInfluence) + dir * flInfluence; + VectorNormalize( dir ); + } // ideal facing direction float idealYaw = UTIL_AngleMod( UTIL_VecToYaw( dir ) ); diff --git a/sp/src/game/server/ai_motor.h b/sp/src/game/server/ai_motor.h index d7f14293..29c05f84 100644 --- a/sp/src/game/server/ai_motor.h +++ b/sp/src/game/server/ai_motor.h @@ -62,6 +62,10 @@ public: virtual void MoveClimbStart( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw ); virtual AIMoveResult_t MoveClimbExecute( const Vector &climbDest, const Vector &climbDir, float climbDist, float yaw, int climbNodesLeft ); virtual void MoveClimbStop(); +#ifdef MAPBASE + virtual void MoveClimbPause(); + virtual bool MoveClimbShouldTeleportToSequenceEnd( Vector &teleportOrigin ); +#endif //--------------------------------- @@ -83,6 +87,9 @@ public: const Vector & GetCurVel() const { return m_vecVelocity; } virtual float OverrideMaxYawSpeed( Activity activity ) { return -1; } +#ifdef MAPBASE + virtual +#endif bool IsDeceleratingToGoal() const { return false; } //--------------------------------- diff --git a/sp/src/game/server/ai_navigator.cpp b/sp/src/game/server/ai_navigator.cpp index d136c923..c58471d0 100644 --- a/sp/src/game/server/ai_navigator.cpp +++ b/sp/src/game/server/ai_navigator.cpp @@ -999,12 +999,23 @@ int CAI_Navigator::GetArrivalSequence( int curSequence ) activity = ACT_IDLE; } - sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( activity ), curSequence ); + Activity translatedActivity = GetOuter()->TranslateActivity( activity ); + sequence = GetOuter()->SelectWeightedSequence( translatedActivity, curSequence ); if ( sequence == ACT_INVALID ) { - DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() ); - sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence ); +#ifdef MAPBASE + if ( translatedActivity == ACT_SCRIPT_CUSTOM_MOVE ) + { + // ACT_SCRIPT_CUSTOM_MOVE allows activity translation to resolve into specific sequences + sequence = GetOuter()->GetScriptCustomMoveSequence(); + } + else +#endif + { + DevMsg( GetOuter(), "No appropriate sequence for arrival activity %s (%d)\n", GetOuter()->GetActivityName( GetPath()->GetArrivalActivity() ), GetPath()->GetArrivalActivity() ); + sequence = GetOuter()->SelectWeightedSequence( GetOuter()->TranslateActivity( ACT_IDLE ), curSequence ); + } } Assert( sequence != ACT_INVALID ); GetPath()->SetArrivalSequence( sequence ); @@ -1642,6 +1653,15 @@ void CAI_Navigator::MoveCalcBaseGoal( AILocalMoveGoal_t *pMoveGoal ) AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint(); if ( pCurWaypoint->GetNext() && pCurWaypoint->GetNext()->NavType() != pCurWaypoint->NavType() ) pMoveGoal->flags |= AILMG_TARGET_IS_TRANSITION; + +#ifdef MAPBASE + // TODO: Better place for this code? + if (pMoveGoal->flags & AILMG_TARGET_IS_TRANSITION && pCurWaypoint->GetNext()->NavType() == NAV_CLIMB) + { + // NPCs need to holster their weapons before climbing. + GetOuter()->SetDesiredWeaponState( DESIREDWEAPONSTATE_HOLSTERED ); + } +#endif } const Task_t *pCurTask = GetOuter()->GetTask(); @@ -2164,11 +2184,26 @@ bool CAI_Navigator::OnMoveBlocked( AIMoveResult_t *pResult ) if (pDoor != NULL) { GetOuter()->OpenPropDoorBegin( pDoor ); +#ifdef MAPBASE + // Tell the navigation to stop running until we're done. + OnNewGoal(); +#endif *pResult = AIMR_OK; return true; } } +#ifdef MAPBASE + if ( GetOuter()->m_hOpeningDoor ) + { + // In the process of opening a door + // Because navigation is now supposed to terminate when a NPC begins opening a door, this code should not be reached. + DbgNavMsg( GetOuter(), "CAI_Navigator::OnMoveBlocked had to check for m_hOpeningDoor\n" ); + *pResult = AIMR_OK; + return true; + } +#endif + // Allow the NPC to override this behavior if ( GetOuter()->OnMoveBlocked( pResult )) @@ -2591,8 +2626,12 @@ bool CAI_Navigator::Move( float flInterval ) if ( GetNavType() == NAV_CLIMB ) { +#ifdef MAPBASE + GetMotor()->MoveClimbPause(); +#else GetMotor()->MoveClimbStop(); SetNavType( NAV_GROUND ); +#endif } GetMotor()->MoveStop(); AssertMsg( TaskIsRunning() || TaskIsComplete(), ("Schedule stalled!!\n") ); @@ -2695,6 +2734,11 @@ void CAI_Navigator::AdvancePath() if (pDoor != NULL) { GetOuter()->OpenPropDoorBegin(pDoor); + +#ifdef MAPBASE + // Tell the navigation to stop running until we're done. + OnNewGoal(); +#endif } else { @@ -3880,7 +3924,12 @@ bool CAI_Navigator::GetStoppingPath( CAI_WaypointList * pClippedWaypoints ) AI_Waypoint_t *pCurWaypoint = GetPath()->GetCurWaypoint(); if ( pCurWaypoint ) { +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + // Since regular climb nav can interrupt itself now, only do this when dismounting + bool bMustCompleteCurrent = ( (pCurWaypoint->NavType() == NAV_CLIMB && (GetActivity() == ACT_CLIMB_DISMOUNT || GetActivity() == ACT_CLIMB_MOUNT_TOP)) || pCurWaypoint->NavType() == NAV_JUMP ); +#else bool bMustCompleteCurrent = ( pCurWaypoint->NavType() == NAV_CLIMB || pCurWaypoint->NavType() == NAV_JUMP ); +#endif float distRemaining = GetMotor()->MinStoppingDist( 0 ); if ( bMustCompleteCurrent ) diff --git a/sp/src/game/server/ai_network.cpp b/sp/src/game/server/ai_network.cpp index 16d8a7fa..1323b7ce 100644 --- a/sp/src/game/server/ai_network.cpp +++ b/sp/src/game/server/ai_network.cpp @@ -144,6 +144,31 @@ public: int m_capabilities; // cache this }; +#ifdef MAPBASE +//------------------------------------- +// Purpose: A version of CNodeFilter which allows hints to influence the result. +//------------------------------------- +class CNodeHintFilter : public CNodeFilter +{ +public: + CNodeHintFilter( CAI_BaseNPC *pNPC, const Vector &pos ) : CNodeFilter( pNPC, pos ) {} + CNodeHintFilter( const Vector &pos ) : CNodeFilter( pos ) {} + + virtual float NodeDistanceSqr( CAI_Node &node ) + { + // Heavier hints are considered closer + if ( node.GetHint() && node.GetHint()->GetHintWeight() != 1.0f && (node.GetHint()->GetGroup() == NULL_STRING || node.GetHint()->GetGroup() == m_pNPC->GetHintGroup()) ) + { + return CNodeFilter::NodeDistanceSqr( node ) * node.GetHint()->GetHintWeightInverse(); + } + else + { + return CNodeFilter::NodeDistanceSqr( node ); + } + } +}; +#endif + //----------------------------------------------------------------------------- // CAI_Network //----------------------------------------------------------------------------- @@ -351,7 +376,12 @@ int CAI_Network::NearestNodeToPoint( CAI_BaseNPC *pNPC, const Vector &vecOrigin, // First get nodes distances and eliminate those that are beyond // the maximum allowed distance for local movements // --------------------------------------------------------------- +#ifdef MAPBASE + // Allow hint weight to influence supposed distance + CNodeHintFilter filter( pNPC, vecOrigin ); +#else CNodeFilter filter( pNPC, vecOrigin ); +#endif #ifdef AI_PERF_MON m_nPerfStatNN++; @@ -605,8 +635,13 @@ CAI_Node *CAI_Network::AddNode( const Vector &origin, float yaw ) CAI_Link *CAI_Network::CreateLink( int srcID, int destID, CAI_DynamicLink *pDynamicLink ) { +#ifdef MAPBASE // From Alien Swarm SDK + CAI_Node *pSrcNode = GetNode( srcID ); + CAI_Node *pDestNode = GetNode( destID ); +#else CAI_Node *pSrcNode = g_pBigAINet->GetNode( srcID ); CAI_Node *pDestNode = g_pBigAINet->GetNode( destID ); +#endif Assert( pSrcNode && pDestNode && pSrcNode != pDestNode ); diff --git a/sp/src/game/server/ai_tacticalservices.cpp b/sp/src/game/server/ai_tacticalservices.cpp index 87399c4f..8bcdcc08 100644 --- a/sp/src/game/server/ai_tacticalservices.cpp +++ b/sp/src/game/server/ai_tacticalservices.cpp @@ -411,7 +411,12 @@ int CAI_TacticalServices::FindCoverNode(const Vector &vNearPos, const Vector &vT // -------------------------------------------------------- pNode->Lock( 1.0 ); +#ifdef MAPBASE + if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW + || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_CUSTOM ) ) +#else if ( pNode->GetHint() && ( pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_MED || pNode->GetHint()->HintType() == HINT_TACTICAL_COVER_LOW ) ) +#endif { if ( GetOuter()->GetHintNode() ) { diff --git a/sp/src/game/server/basecombatcharacter.cpp b/sp/src/game/server/basecombatcharacter.cpp index 5f61ae37..c3927156 100644 --- a/sp/src/game/server/basecombatcharacter.cpp +++ b/sp/src/game/server/basecombatcharacter.cpp @@ -61,11 +61,6 @@ extern int g_interactionBarnacleVictimReleased; #endif //HL2_DLL -#ifdef MAPBASE -extern acttable_t *GetSMG1Acttable(); -extern int GetSMG1ActtableCount(); -#endif - extern ConVar weapon_showproficiency; ConVar ai_show_hull_attacks( "ai_show_hull_attacks", "0" ); @@ -2751,19 +2746,22 @@ Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool we return activity; } - acttable_t *pTable = GetSMG1Acttable(); - int actCount = GetSMG1ActtableCount(); - for ( int i = 0; i < actCount; i++, pTable++ ) + acttable_t *pTable = pWeapon->GetBackupActivityList(); + if (pTable) { - if ( activity == pTable->baseAct ) + int actCount = pWeapon->GetBackupActivityListCount(); + for ( int i = 0; i < actCount; i++, pTable++ ) { - // Don't pick SMG animations we don't actually have an animation for. - if (GetModelPtr() ? !GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct) : false) + if ( activity == pTable->baseAct ) { - return activity; - } + // Don't pick SMG animations we don't actually have an animation for. + if (GetModelPtr() ? !GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct) : false) + { + return activity; + } - return (Activity)pTable->weaponAct; + return (Activity)pTable->weaponAct; + } } } diff --git a/sp/src/game/server/hl2/ai_behavior_functank.cpp b/sp/src/game/server/hl2/ai_behavior_functank.cpp index f0a32601..89f79228 100644 --- a/sp/src/game/server/hl2/ai_behavior_functank.cpp +++ b/sp/src/game/server/hl2/ai_behavior_functank.cpp @@ -110,6 +110,28 @@ bool CAI_FuncTankBehavior::IsInterruptable( void ) return BaseClass::IsInterruptable(); } + +ConVar ai_tank_allow_expanded_npcs( "ai_tank_allow_expanded_npcs", "1", FCVAR_NONE, "Allows Father Grigori, Barney, and vortigaunts to automatically man func_tanks." ); + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CAI_FuncTankBehavior::CanManTank( CFuncTank *pTank, bool bForced ) +{ + if (!bForced) + { + // In order to prevent potential problems in existing maps, Father Grigori, Barney, and vortigaunts can be set to not automatically man func_tanks by default. + if (ai_tank_allow_expanded_npcs.GetBool() == false) + { + const char *pszClass = GetOuter()->GetClassname(); + if ( FStrEq( pszClass, "npc_monk" ) || FStrEq( pszClass, "npc_barney" ) || FStrEq( pszClass, "npc_vortigaunt" ) ) + return false; + } + } + + return true; +} #endif //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/ai_behavior_functank.h b/sp/src/game/server/hl2/ai_behavior_functank.h index a9f19496..75d5b8df 100644 --- a/sp/src/game/server/hl2/ai_behavior_functank.h +++ b/sp/src/game/server/hl2/ai_behavior_functank.h @@ -51,6 +51,8 @@ public: void PrescheduleThink(); #ifdef MAPBASE bool IsInterruptable( void ); + + bool CanManTank( CFuncTank *pTank, bool bForced ); #endif Activity NPC_TranslateActivity( Activity activity ); diff --git a/sp/src/game/server/hl2/func_tank.cpp b/sp/src/game/server/hl2/func_tank.cpp index 963a0e54..c4462575 100644 --- a/sp/src/game/server/hl2/func_tank.cpp +++ b/sp/src/game/server/hl2/func_tank.cpp @@ -402,7 +402,11 @@ void CFuncTank::InputFindNPCToManTank( inputdata_t &inputdata ) { // Verify the npc has the func_tank controller behavior. CAI_FuncTankBehavior *pBehavior; +#ifdef MAPBASE + if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) ) +#else if ( pNPC->GetBehavior( &pBehavior ) ) +#endif { m_hController = pNPC; pBehavior->SetFuncTank( this ); @@ -439,7 +443,7 @@ void CFuncTank::InputTeleportNPCToManTank( inputdata_t &inputdata ) { // Verify the npc has the func_tank controller behavior. CAI_FuncTankBehavior *pBehavior; - if ( pNPC->GetBehavior( &pBehavior ) ) + if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) ) { Vector vecVec; QAngle angAng; @@ -512,7 +516,7 @@ void CFuncTank::InputForceNPCToManTank( inputdata_t &inputdata ) { // Verify the npc has the func_tank controller behavior. CAI_FuncTankBehavior *pBehavior; - if ( pNPC->GetBehavior( &pBehavior ) ) + if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, true ) ) { // Set the forced condition pBehavior->SetCondition( CAI_FuncTankBehavior::COND_FUNCTANK_FORCED ); @@ -627,7 +631,11 @@ void CFuncTank::NPC_FindController( void ) continue; CAI_FuncTankBehavior *pBehavior; +#ifdef MAPBASE + if ( pNPC->GetBehavior( &pBehavior ) && pBehavior->CanManTank( this, false ) ) +#else if ( pNPC->GetBehavior( &pBehavior ) ) +#endif { // Don't mount the func_tank if your "enemy" is within X feet or it or the npc. CBaseEntity *pEnemy = pNPC->GetEnemy(); diff --git a/sp/src/game/server/hl2/npc_alyx_episodic.cpp b/sp/src/game/server/hl2/npc_alyx_episodic.cpp index 42c2eb3f..9df7a3ee 100644 --- a/sp/src/game/server/hl2/npc_alyx_episodic.cpp +++ b/sp/src/game/server/hl2/npc_alyx_episodic.cpp @@ -156,6 +156,8 @@ ConVar npc_alyx_crouch( "npc_alyx_crouch", "1" ); #ifdef MAPBASE ConVar npc_alyx_interact_manhacks( "npc_alyx_interact_manhacks", "1" ); ConVar npc_alyx_interact_turrets( "npc_alyx_interact_turrets", "0" ); + +ConVar npc_alyx_allow_fly( "npc_alyx_allow_fly", "0", FCVAR_NONE, "Allows Alyx to use FL_FLY outside of scripted sequences, actbusy, or navigation." ); #endif // global pointer to Alyx for fast lookups @@ -319,7 +321,9 @@ CNPC_Alyx *CNPC_Alyx::GetAlyx( void ) //========================================================= bool CNPC_Alyx::CreateBehaviors() { +#ifndef MAPBASE // Moved to CNPC_PlayerCompanion AddBehavior( &m_FuncTankBehavior ); +#endif bool result = BaseClass::CreateBehaviors(); return result; @@ -885,7 +889,11 @@ void CNPC_Alyx::GatherConditions() // ROBIN: This was here to solve a problem in a playtest. We've since found what we think was the cause. // It's a useful piece of debug to have lying there, so I've left it in. - if ( (GetFlags() & FL_FLY) && m_NPCState != NPC_STATE_SCRIPT && !m_ActBusyBehavior.IsActive() && !m_PassengerBehavior.IsEnabled() ) + if ( (GetFlags() & FL_FLY) && m_NPCState != NPC_STATE_SCRIPT && !m_ActBusyBehavior.IsActive() && !m_PassengerBehavior.IsEnabled() +#ifdef MAPBASE + && GetNavType() != NAV_CLIMB && !npc_alyx_allow_fly.GetBool() +#endif + ) { Warning( "Removed FL_FLY from Alyx, who wasn't running a script or actbusy. Time %.2f, map %s.\n", gpGlobals->curtime, STRING(gpGlobals->mapname) ); RemoveFlag( FL_FLY ); @@ -1687,9 +1695,88 @@ Activity CNPC_Alyx::NPC_TranslateActivity( Activity activity ) case ACT_DROP_WEAPON: if ( HasShotgun() ) return (Activity)ACT_DROP_WEAPON_SHOTGUN; } +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Alyx has her own pistol readiness animations which use the default activities + switch (activity) + { + case ACT_IDLE_PISTOL_RELAXED: + return ACT_IDLE_RELAXED; + case ACT_IDLE_PISTOL_STIMULATED: + return ACT_IDLE_STIMULATED; + case ACT_WALK_PISTOL_RELAXED: + return ACT_WALK; + case ACT_WALK_PISTOL_STIMULATED: + return ACT_WALK_PISTOL; + case ACT_RUN_PISTOL_RELAXED: + return ACT_RUN; + case ACT_RUN_PISTOL_STIMULATED: + return ACT_RUN_PISTOL; + } +#endif + return activity; } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CNPC_Alyx::Weapon_TranslateActivity( Activity activity, bool *pRequired ) +{ + activity = BaseClass::Weapon_TranslateActivity( activity, pRequired ); + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Alyx has her own pistol readiness animations which use the default activities + switch (activity) + { + case ACT_IDLE_PISTOL_RELAXED: + return ACT_IDLE_RELAXED; + case ACT_IDLE_PISTOL_STIMULATED: + return ACT_IDLE_STIMULATED; + case ACT_WALK_PISTOL_RELAXED: + return ACT_WALK; + case ACT_WALK_PISTOL_STIMULATED: + return ACT_WALK_PISTOL; + case ACT_RUN_PISTOL_RELAXED: + return ACT_RUN; + case ACT_RUN_PISTOL_STIMULATED: + return ACT_RUN_PISTOL; + } +#endif + + return activity; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CNPC_Alyx::Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pSpecificWeapon ) +{ + activity = BaseClass::Weapon_BackupActivity( activity, weaponTranslationWasRequired, pSpecificWeapon ); + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Alyx has her own pistol readiness animations which use the default activities + switch (activity) + { + case ACT_IDLE_PISTOL_RELAXED: + return ACT_IDLE_RELAXED; + case ACT_IDLE_PISTOL_STIMULATED: + return ACT_IDLE_STIMULATED; + case ACT_WALK_PISTOL_RELAXED: + return ACT_WALK; + case ACT_WALK_PISTOL_STIMULATED: + return ACT_WALK_PISTOL; + case ACT_RUN_PISTOL_RELAXED: + return ACT_RUN; + case ACT_RUN_PISTOL_STIMULATED: + return ACT_RUN_PISTOL; + } +#endif + + return activity; +} +#endif + bool CNPC_Alyx::ShouldDeferToFollowBehavior() { return BaseClass::ShouldDeferToFollowBehavior(); diff --git a/sp/src/game/server/hl2/npc_alyx_episodic.h b/sp/src/game/server/hl2/npc_alyx_episodic.h index 6d4eb67b..f24ddf67 100644 --- a/sp/src/game/server/hl2/npc_alyx_episodic.h +++ b/sp/src/game/server/hl2/npc_alyx_episodic.h @@ -91,6 +91,10 @@ public: bool CanSeeEntityInDarkness( CBaseEntity *pEntity ); bool IsCoverPosition( const Vector &vecThreat, const Vector &vecPosition ); Activity NPC_TranslateActivity ( Activity activity ); +#ifdef MAPBASE + Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL ); + Activity Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired = false, CBaseCombatWeapon *pSpecificWeapon = NULL ); +#endif bool ShouldDeferToFollowBehavior(); void BuildScheduleTestBits(); bool ShouldBehaviorSelectSchedule( CAI_BehaviorBase *pBehavior ); @@ -231,7 +235,9 @@ private: bool m_bShouldHaveEMP; +#ifndef MAPBASE // Moved to CNPC_PlayerCompanion CAI_FuncTankBehavior m_FuncTankBehavior; +#endif COutputEvent m_OnFinishInteractWithObject; COutputEvent m_OnPlayerUse; diff --git a/sp/src/game/server/hl2/npc_citizen17.cpp b/sp/src/game/server/hl2/npc_citizen17.cpp index d14fc702..ab7647c6 100644 --- a/sp/src/game/server/hl2/npc_citizen17.cpp +++ b/sp/src/game/server/hl2/npc_citizen17.cpp @@ -439,10 +439,11 @@ CSimpleSimTimer CNPC_Citizen::gm_PlayerSquadEvaluateTimer; bool CNPC_Citizen::CreateBehaviors() { BaseClass::CreateBehaviors(); - AddBehavior( &m_FuncTankBehavior ); #ifdef MAPBASE AddBehavior( &m_RappelBehavior ); AddBehavior( &m_PolicingBehavior ); +#else // Moved to CNPC_PlayerCompanion + AddBehavior( &m_FuncTankBehavior ); #endif return true; @@ -2085,6 +2086,13 @@ Activity CNPC_Citizen::NPC_TranslateActivity( Activity activity ) return ACT_RUN_AIM_AR2_STIMULATED; if (activity == ACT_WALK_AIM_AR2) return ACT_WALK_AIM_AR2_STIMULATED; + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + if (activity == ACT_RUN_AIM_PISTOL) + return ACT_RUN_AIM_PISTOL_STIMULATED; + if (activity == ACT_WALK_AIM_PISTOL) + return ACT_WALK_AIM_PISTOL_STIMULATED; +#endif } #endif diff --git a/sp/src/game/server/hl2/npc_citizen17.h b/sp/src/game/server/hl2/npc_citizen17.h index 6828a664..5d23429d 100644 --- a/sp/src/game/server/hl2/npc_citizen17.h +++ b/sp/src/game/server/hl2/npc_citizen17.h @@ -370,7 +370,6 @@ private: #endif //----------------------------------------------------- - CAI_FuncTankBehavior m_FuncTankBehavior; #ifdef MAPBASE CAI_RappelBehavior m_RappelBehavior; CAI_PolicingBehavior m_PolicingBehavior; @@ -378,6 +377,8 @@ private: // Rappel virtual bool IsWaitingToRappel( void ) { return m_RappelBehavior.IsWaitingToRappel(); } void BeginRappel() { m_RappelBehavior.BeginRappel(); } +#else // Moved to CNPC_PlayerCompanion + CAI_FuncTankBehavior m_FuncTankBehavior; #endif CHandle m_hSavedFollowGoalEnt; diff --git a/sp/src/game/server/hl2/npc_combine.cpp b/sp/src/game/server/hl2/npc_combine.cpp index 3076347d..ebeccf2b 100644 --- a/sp/src/game/server/hl2/npc_combine.cpp +++ b/sp/src/game/server/hl2/npc_combine.cpp @@ -43,6 +43,7 @@ int g_fCombineQuestion; // true if an idle grunt asked a question. Cleared wh #ifdef MAPBASE ConVar npc_combine_idle_walk_easy( "npc_combine_idle_walk_easy", "1", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use ACT_WALK_EASY as a walking animation when idle." ); ConVar npc_combine_unarmed_anims( "npc_combine_unarmed_anims", "1", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use unarmed idle/walk animations when they have no weapon." ); +ConVar npc_combine_protected_run( "npc_combine_protected_run", "0", FCVAR_NONE, "Mapbase: Allows Combine soldiers to use \"protected run\" animations." ); ConVar npc_combine_altfire_not_allies_only( "npc_combine_altfire_not_allies_only", "1", FCVAR_NONE, "Mapbase: Elites are normally only allowed to fire their alt-fire attack at the player and the player's allies; This allows elites to alt-fire at other enemies too." ); ConVar npc_combine_new_cover_behavior( "npc_combine_new_cover_behavior", "1", FCVAR_NONE, "Mapbase: Toggles small patches for parts of npc_combine AI related to soldiers failing to take cover. These patches are minimal and only change cases where npc_combine would otherwise look at an enemy without shooting or run up to the player to melee attack when they don't have to. Consult the Mapbase wiki for more information." ); @@ -127,8 +128,9 @@ Activity ACT_COMBINE_AR2_ALTFIRE; Activity ACT_WALK_EASY; Activity ACT_WALK_MARCH; #ifdef MAPBASE -Activity ACT_IDLE_UNARMED; -Activity ACT_WALK_UNARMED; +Activity ACT_TURRET_CARRY_IDLE; +Activity ACT_TURRET_CARRY_WALK; +Activity ACT_TURRET_CARRY_RUN; #endif // ----------------------------------------------- @@ -544,7 +546,12 @@ void CNPC_Combine::GatherConditions() if( GetState() == NPC_STATE_COMBAT ) { +#ifdef MAPBASE + // Don't override the standoff + if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) && !m_StandoffBehavior.IsActive() ) +#else if( IsCurSchedule( SCHED_COMBINE_WAIT_IN_COVER, false ) ) +#endif { // Soldiers that are standing around doing nothing poll for attack slots so // that they can respond quickly when one comes available. If they can @@ -1540,11 +1547,6 @@ void CNPC_Combine::BuildScheduleTestBits( void ) //----------------------------------------------------------------------------- Activity CNPC_Combine::Weapon_TranslateActivity( Activity eNewActivity, bool *pRequired ) { - // We have differing low animations and ACT_CROUCHIDLE is not friendly to weapon translation. - // ACT_CROUCHIDLE is pretty much deprecated at this point anyway. - if (eNewActivity == ACT_CROUCHIDLE) - eNewActivity = ACT_RANGE_AIM_LOW; - return BaseClass::Weapon_TranslateActivity(eNewActivity, pRequired); } @@ -1553,12 +1555,6 @@ Activity CNPC_Combine::Weapon_TranslateActivity( Activity eNewActivity, bool *pR //----------------------------------------------------------------------------- Activity CNPC_Combine::NPC_BackupActivity( Activity eNewActivity ) { - // Otherwise we move around, T-posing. - if (eNewActivity == ACT_WALK) - return ACT_WALK_UNARMED; - else if (eNewActivity == ACT_RUN) - return ACT_RUN_RIFLE; - // Some models might not contain ACT_COMBINE_BUGBAIT, which the soldier model uses instead of ACT_IDLE_ON_FIRE. // Contrariwise, soldiers may be called to use ACT_IDLE_ON_FIRE in other parts of the AI and need to translate to ACT_COMBINE_BUGBAIT. if (eNewActivity == ACT_COMBINE_BUGBAIT) @@ -1630,16 +1626,32 @@ Activity CNPC_Combine::NPC_TranslateActivity( Activity eNewActivity ) } } #ifdef MAPBASE - else if (!GetActiveWeapon() && npc_combine_unarmed_anims.GetBool() && HaveSequenceForActivity(ACT_IDLE_UNARMED)) + else if (!GetActiveWeapon() && !npc_combine_unarmed_anims.GetBool()) { if (eNewActivity == ACT_IDLE || eNewActivity == ACT_IDLE_ANGRY) - eNewActivity = ACT_IDLE_UNARMED; + eNewActivity = ACT_IDLE_SMG1; else if (eNewActivity == ACT_WALK) - eNewActivity = ACT_WALK_UNARMED; + eNewActivity = ACT_WALK_RIFLE; + else if (eNewActivity == ACT_RUN) + eNewActivity = ACT_RUN_RIFLE; } - else if (eNewActivity == ACT_WALK && m_NPCState == NPC_STATE_IDLE && npc_combine_idle_walk_easy.GetBool() && HaveSequenceForActivity(ACT_WALK_EASY)) + else if (m_NPCState == NPC_STATE_IDLE && eNewActivity == ACT_WALK) { - eNewActivity = ACT_WALK_EASY; + if (npc_combine_idle_walk_easy.GetBool()) + { + // ACT_WALK_EASY has been replaced with ACT_WALK_RELAXED for weapon translation purposes + eNewActivity = ACT_WALK_RELAXED; + } + else if (GetActiveWeapon()) + { + eNewActivity = ACT_WALK_RIFLE; + } + } + + if ( eNewActivity == ACT_RUN && ( IsCurSchedule( SCHED_TAKE_COVER_FROM_BEST_SOUND ) || IsCurSchedule( SCHED_FLEE_FROM_BEST_SOUND ) ) ) + { + if ( random->RandomInt( 0, 1 ) && npc_combine_protected_run.GetBool() && HaveSequenceForActivity( ACT_RUN_PROTECTED ) ) + eNewActivity = ACT_RUN_PROTECTED; } #endif @@ -2606,7 +2618,6 @@ int CNPC_Combine::TranslateSchedule( int scheduleType ) #ifdef MAPBASE // SCHED_COMBINE_WAIT_IN_COVER uses INCOVER, but only gets out of it when the soldier moves. // That seems to mess up shooting, so this Forget() attempts to fix that. - // I don't know if there's a better workaround. Forget( bits_MEMORY_INCOVER ); #endif @@ -3791,6 +3802,13 @@ WeaponProficiency_t CNPC_Combine::CalcWeaponProficiency( CBaseCombatWeapon *pWea { return WEAPON_PROFICIENCY_GOOD; } +#ifdef MAPBASE + else if ( pWeapon->ClassMatches( gm_isz_class_Pistol ) ) + { + // Mods which need a lower soldier pistol accuracy can either change this value or use proficiency override in Hammer. + return WEAPON_PROFICIENCY_VERY_GOOD; + } +#endif return BaseClass::CalcWeaponProficiency( pWeapon ); } @@ -3906,7 +3924,12 @@ bool CNPC_Combine::IsRunningApproachEnemySchedule() bool CNPC_Combine::ShouldPickADeathPose( void ) { +#ifdef MAPBASE + // Check base class as well + return !IsCrouching() && BaseClass::ShouldPickADeathPose(); +#else return !IsCrouching(); +#endif } //----------------------------------------------------------------------------- @@ -3940,8 +3963,9 @@ DECLARE_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE ) DECLARE_ACTIVITY( ACT_WALK_EASY ) DECLARE_ACTIVITY( ACT_WALK_MARCH ) #ifdef MAPBASE -DECLARE_ACTIVITY( ACT_IDLE_UNARMED ) -DECLARE_ACTIVITY( ACT_WALK_UNARMED ) +DECLARE_ACTIVITY( ACT_TURRET_CARRY_IDLE ) +DECLARE_ACTIVITY( ACT_TURRET_CARRY_WALK ) +DECLARE_ACTIVITY( ACT_TURRET_CARRY_RUN ) #endif DECLARE_ANIMEVENT( COMBINE_AE_BEGIN_ALTFIRE ) diff --git a/sp/src/game/server/hl2/npc_fastzombie.cpp b/sp/src/game/server/hl2/npc_fastzombie.cpp index 0e15e2a8..be53f344 100644 --- a/sp/src/game/server/hl2/npc_fastzombie.cpp +++ b/sp/src/game/server/hl2/npc_fastzombie.cpp @@ -1296,7 +1296,11 @@ void CFastZombie::StartTask( const Task_t *pTask ) CBaseEntity *pEnemy = GetEnemy(); Vector vecJumpDir; - if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN ) + if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + || GetActivity() == ACT_CLIMB_ALL +#endif + ) { // Jump off the pipe backwards! Vector forward; @@ -1449,7 +1453,11 @@ int CFastZombie::TranslateSchedule( int scheduleType ) break; case SCHED_FASTZOMBIE_UNSTICK_JUMP: - if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT ) + if ( GetActivity() == ACT_CLIMB_UP || GetActivity() == ACT_CLIMB_DOWN || GetActivity() == ACT_CLIMB_DISMOUNT +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + || (GetActivity() >= ACT_CLIMB_ALL && GetActivity() <= ACT_CLIMB_DISMOUNT_BOTTOM) +#endif + ) { return SCHED_FASTZOMBIE_CLIMBING_UNSTICK_JUMP; } @@ -1477,8 +1485,10 @@ int CFastZombie::TranslateSchedule( int scheduleType ) //--------------------------------------------------------- Activity CFastZombie::NPC_TranslateActivity( Activity baseAct ) { +#ifndef MAPBASE // Now covered by CAI_BaseNPC::NPC_BackupActivity if ( baseAct == ACT_CLIMB_DOWN ) return ACT_CLIMB_UP; +#endif return BaseClass::NPC_TranslateActivity( baseAct ); } diff --git a/sp/src/game/server/hl2/npc_metropolice.cpp b/sp/src/game/server/hl2/npc_metropolice.cpp index b3b0e5d1..a8579647 100644 --- a/sp/src/game/server/hl2/npc_metropolice.cpp +++ b/sp/src/game/server/hl2/npc_metropolice.cpp @@ -1537,7 +1537,12 @@ void CNPC_MetroPolice::OnUpdateShotRegulator( ) BaseClass::OnUpdateShotRegulator(); // FIXME: This code (except the burst interval) could be used for all weapon types +#ifdef MAPBASE + // Only if we actually have the pistol out + if ( EntIsClass( GetActiveWeapon(), gm_isz_class_Pistol ) ) +#else if( Weapon_OwnsThisType( "weapon_pistol" ) ) +#endif { if ( m_nBurstMode == BURST_NOT_ACTIVE ) { @@ -3899,6 +3904,15 @@ int CNPC_MetroPolice::SelectScheduleNoDirectEnemy() return SCHED_METROPOLICE_SMASH_PROP; } +#ifdef MAPBASE + // If you see your enemy and you're still arming yourself, wait and don't just charge in + // (if your weapon is holstered, you're probably about to arm yourself) + if ( HasCondition( COND_SEE_ENEMY ) && GetWeapon(0) && (IsWeaponHolstered() || FindGestureLayer( TranslateActivity( ACT_ARM ) ) != -1) ) + { + return SCHED_COMBAT_FACE; + } +#endif + return SCHED_METROPOLICE_CHASE_ENEMY; } @@ -4522,34 +4536,7 @@ int CNPC_MetroPolice::SelectBehaviorOverrideSchedule() //----------------------------------------------------------------------------- bool CNPC_MetroPolice::IsCrouchedActivity( Activity activity ) { - Activity realActivity = TranslateActivity(activity); - - switch ( realActivity ) - { - case ACT_RELOAD_LOW: - case ACT_COVER_LOW: - case ACT_COVER_PISTOL_LOW: - case ACT_COVER_SMG1_LOW: - case ACT_RELOAD_SMG1_LOW: - //case ACT_RELOAD_AR2_LOW: - case ACT_RELOAD_PISTOL_LOW: - case ACT_RELOAD_SHOTGUN_LOW: - - // These animations aren't actually "low" on metrocops - //case ACT_RANGE_AIM_LOW: - //case ACT_RANGE_AIM_AR2_LOW: - //case ACT_RANGE_AIM_SMG1_LOW: - //case ACT_RANGE_AIM_PISTOL_LOW: - - //case ACT_RANGE_ATTACK1_LOW: - //case ACT_RANGE_ATTACK_AR2_LOW: - //case ACT_RANGE_ATTACK_SMG1_LOW: - //case ACT_RANGE_ATTACK_PISTOL_LOW: - //case ACT_RANGE_ATTACK2_LOW: - return true; - } - - return false; + return BaseClass::IsCrouchedActivity( activity ); } //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_playercompanion.cpp b/sp/src/game/server/hl2/npc_playercompanion.cpp index 597f4c7f..97901f93 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.cpp +++ b/sp/src/game/server/hl2/npc_playercompanion.cpp @@ -61,6 +61,10 @@ int AE_COMPANION_RELEASE_FLARE; #define COMPANION_MELEE_DIST 64.0 #endif +#ifdef MAPBASE +ConVar ai_allow_new_weapons( "ai_allow_new_weapons", "1", FCVAR_NONE, "Allows companion NPCs to automatically pick up and use weapons they were unable pick up before, i.e. 357s or crossbows." ); +#endif + #define MAX_TIME_BETWEEN_BARRELS_EXPLODING 5.0f #define MAX_TIME_BETWEEN_CONSECUTIVE_PLAYER_KILLS 3.0f @@ -211,6 +215,10 @@ bool CNPC_PlayerCompanion::CreateBehaviors() AddBehavior( &m_FollowBehavior ); AddBehavior( &m_LeadBehavior ); #endif//HL2_EPISODIC + +#ifdef MAPBASE + AddBehavior( &m_FuncTankBehavior ); +#endif return BaseClass::CreateBehaviors(); } @@ -1642,6 +1650,19 @@ Activity CNPC_PlayerCompanion::TranslateActivityReadiness( Activity activity ) continue; } +#ifdef MAPBASE + // If we don't have the readiness activity we selected and there's no backup activity available, break the loop and return the base act. + bool bRequired; + if ( !HaveSequenceForActivity( actremap.mappedActivity ) && !HaveSequenceForActivity( Weapon_TranslateActivity( actremap.mappedActivity, &bRequired ) ) ) + { + Activity backupAct = Weapon_BackupActivity( actremap.mappedActivity, bRequired ); + if ( backupAct != actremap.mappedActivity ) + return backupAct; + else + break; + } +#endif + // We've successfully passed all criteria for remapping this return actremap.mappedActivity; } @@ -1665,21 +1686,6 @@ Activity CNPC_PlayerCompanion::NPC_TranslateActivity( Activity activity ) activity = ACT_RUN_PROTECTED; } -#ifdef COMPANION_HOLSTER_WORKAROUND - if (activity == ACT_DISARM || activity == ACT_ARM) - { - CBaseCombatWeapon *pWeapon = GetActiveWeapon() ? GetActiveWeapon() : m_hWeapons[m_iLastHolsteredWeapon]; - if (pWeapon && pWeapon->WeaponClassify() != WEPCLASS_HANDGUN) - { - switch (activity) - { - case ACT_DISARM: return ACT_DISARM_RIFLE; - case ACT_ARM: return ACT_ARM_RIFLE; - } - } - } -#endif - activity = BaseClass::NPC_TranslateActivity( activity ); if ( activity == ACT_IDLE ) @@ -2748,6 +2754,13 @@ bool CNPC_PlayerCompanion::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) { return (NumWeaponsInSquad("weapon_shotgun") < 1 ); } +#ifdef MAPBASE + else if (EntIsClass( pWeapon, gm_isz_class_Pistol ) || EntIsClass( pWeapon, gm_isz_class_357 ) || EntIsClass( pWeapon, gm_isz_class_Crossbow )) + { + // The AI automatically detects these weapons as usable now that there's animations for them, so ensure this behavior can be toggled in situations where that's not desirable + return ai_allow_new_weapons.GetBool(); + } +#endif else { return true; diff --git a/sp/src/game/server/hl2/npc_playercompanion.h b/sp/src/game/server/hl2/npc_playercompanion.h index 2fe0b843..ce23058b 100644 --- a/sp/src/game/server/hl2/npc_playercompanion.h +++ b/sp/src/game/server/hl2/npc_playercompanion.h @@ -23,6 +23,7 @@ #endif #ifdef MAPBASE +#include "ai_behavior_functank.h" #include "mapbase/ai_grenade.h" #endif @@ -432,6 +433,9 @@ protected: CAI_OperatorBehavior m_OperatorBehavior; CAI_PassengerBehaviorCompanion m_PassengerBehavior; CAI_FearBehavior m_FearBehavior; +#endif +#ifdef MAPBASE + CAI_FuncTankBehavior m_FuncTankBehavior; #endif //----------------------------------------------------- diff --git a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp index 7fdd89f2..839c18d9 100644 --- a/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp +++ b/sp/src/game/server/hl2/npc_vortigaunt_episodic.cpp @@ -2718,6 +2718,15 @@ void CNPC_Vortigaunt::OnSquishedGrub( const CBaseEntity *pGrub ) //----------------------------------------------------------------------------- void CNPC_Vortigaunt::AimGun( void ) { +#ifdef MAPBASE + // Use base for func_tank + if (m_FuncTankBehavior.IsRunning()) + { + BaseClass::AimGun(); + return; + } +#endif + // If our aim lock is on, don't bother if ( m_flAimDelay >= gpGlobals->curtime ) return; diff --git a/sp/src/game/server/hl2/proto_sniper.cpp b/sp/src/game/server/hl2/proto_sniper.cpp index 64f11d2a..84eba6df 100644 --- a/sp/src/game/server/hl2/proto_sniper.cpp +++ b/sp/src/game/server/hl2/proto_sniper.cpp @@ -250,6 +250,10 @@ public: virtual int SelectSchedule( void ); virtual int TranslateSchedule( int scheduleType ); +#ifdef MAPBASE + Activity NPC_TranslateActivity( Activity eNewActivity ); +#endif + bool KeyValue( const char *szKeyName, const char *szValue ); void PrescheduleThink( void ); @@ -2036,6 +2040,23 @@ int CProtoSniper::TranslateSchedule( int scheduleType ) return BaseClass::TranslateSchedule( scheduleType ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CProtoSniper::NPC_TranslateActivity( Activity eNewActivity ) +{ + // ACT_IDLE is now just the soldier's unarmed idle animation. + // Use a gun-holding animation like what unhidden snipers were using before. + if (!HasSpawnFlags( SF_SNIPER_HIDDEN ) && eNewActivity == ACT_IDLE) + { + eNewActivity = ACT_IDLE_SMG1; + } + + return BaseClass::NPC_TranslateActivity( eNewActivity ); +} +#endif + //--------------------------------------------------------- //--------------------------------------------------------- void CProtoSniper::ScopeGlint() diff --git a/sp/src/game/server/hl2/weapon_357.cpp b/sp/src/game/server/hl2/weapon_357.cpp index 59237c84..8b4fe93f 100644 --- a/sp/src/game/server/hl2/weapon_357.cpp +++ b/sp/src/game/server/hl2/weapon_357.cpp @@ -27,6 +27,11 @@ // CWeapon357 //----------------------------------------------------------------------------- +#ifdef MAPBASE +extern acttable_t *GetPistolActtable(); +extern int GetPistolActtableCount(); +#endif + class CWeapon357 : public CBaseHLCombatWeapon { DECLARE_CLASS( CWeapon357, CBaseHLCombatWeapon ); @@ -69,6 +74,9 @@ public: void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + + virtual acttable_t *GetBackupActivityList() { return GetPistolActtable(); } + virtual int GetBackupActivityListCount() { return GetPistolActtableCount(); } #endif DECLARE_SERVERCLASS(); @@ -91,6 +99,22 @@ END_DATADESC() #ifdef MAPBASE acttable_t CWeapon357::m_acttable[] = { +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE, ACT_IDLE_REVOLVER, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_REVOLVER, true }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_REVOLVER, true }, + { ACT_RELOAD, ACT_RELOAD_REVOLVER, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_REVOLVER, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_REVOLVER, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_REVOLVER, true }, + { ACT_RELOAD_LOW, ACT_RELOAD_REVOLVER_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_REVOLVER_LOW, false }, + { ACT_COVER_LOW, ACT_COVER_REVOLVER_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_REVOLVER_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_REVOLVER, false }, + { ACT_WALK, ACT_WALK_REVOLVER, true }, + { ACT_RUN, ACT_RUN_REVOLVER, true }, +#else { ACT_IDLE, ACT_IDLE_PISTOL, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true }, { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true }, @@ -105,24 +129,40 @@ acttable_t CWeapon357::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false }, { ACT_WALK, ACT_WALK_PISTOL, false }, { ACT_RUN, ACT_RUN_PISTOL, false }, +#endif // // Activities ported from weapon_alyxgun below // // Readiness activities (not aiming) +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, +#else { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, +#endif { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, +#else { ACT_WALK_RELAXED, ACT_WALK, false },//never aims { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false }, +#endif { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, - + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, +#else { ACT_RUN_RELAXED, ACT_RUN, false },//never aims { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false }, +#endif { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, @@ -153,6 +193,11 @@ acttable_t CWeapon357::m_acttable[] = { ACT_READINESS_RELAXED_TO_STIMULATED_WALK, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED_WALK, false }, { ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false }, { ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false }, + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_REVOLVER_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_REVOLVER_MED, false }, +#endif }; diff --git a/sp/src/game/server/hl2/weapon_alyxgun.cpp b/sp/src/game/server/hl2/weapon_alyxgun.cpp index 9c093be6..538f6312 100644 --- a/sp/src/game/server/hl2/weapon_alyxgun.cpp +++ b/sp/src/game/server/hl2/weapon_alyxgun.cpp @@ -44,18 +44,33 @@ acttable_t CWeaponAlyxGun::m_acttable[] = #endif // Readiness activities (not aiming) +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, +#else { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, +#endif { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, +#else { ACT_WALK_RELAXED, ACT_WALK, false },//never aims { ACT_WALK_STIMULATED, ACT_WALK_STIMULATED, false }, +#endif { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, - + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, +#else { ACT_RUN_RELAXED, ACT_RUN, false },//never aims { ACT_RUN_STIMULATED, ACT_RUN_STIMULATED, false }, +#endif { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, diff --git a/sp/src/game/server/hl2/weapon_alyxgun.h b/sp/src/game/server/hl2/weapon_alyxgun.h index cc3e862c..4dd95532 100644 --- a/sp/src/game/server/hl2/weapon_alyxgun.h +++ b/sp/src/game/server/hl2/weapon_alyxgun.h @@ -13,6 +13,11 @@ #pragma once #endif +#ifdef MAPBASE +extern acttable_t *GetPistolActtable(); +extern int GetPistolActtableCount(); +#endif + class CWeaponAlyxGun : public CHLSelectFireMachineGun { DECLARE_DATADESC(); @@ -51,6 +56,11 @@ public: SetTouch(NULL); } +#ifdef MAPBASE + virtual acttable_t *GetBackupActivityList() { return GetPistolActtable(); } + virtual int GetBackupActivityListCount() { return GetPistolActtableCount(); } +#endif + float m_flTooCloseTimer; DECLARE_ACTTABLE(); diff --git a/sp/src/game/server/hl2/weapon_ar2.cpp b/sp/src/game/server/hl2/weapon_ar2.cpp index 89a16700..e6a32e93 100644 --- a/sp/src/game/server/hl2/weapon_ar2.cpp +++ b/sp/src/game/server/hl2/weapon_ar2.cpp @@ -38,10 +38,6 @@ ConVar sk_weapon_ar2_alt_fire_radius( "sk_weapon_ar2_alt_fire_radius", "10" ); ConVar sk_weapon_ar2_alt_fire_duration( "sk_weapon_ar2_alt_fire_duration", "2" ); ConVar sk_weapon_ar2_alt_fire_mass( "sk_weapon_ar2_alt_fire_mass", "150" ); -#ifdef MAPBASE -extern acttable_t *GetSMG1Acttable(); -#endif - //========================================================= //========================================================= @@ -100,14 +96,14 @@ acttable_t CWeaponAR2::m_acttable[] = { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, { ACT_RUN, ACT_RUN_AR2, true }, - { ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_AR2, true }, { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_AR2, false }, - { ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false }, + { ACT_COVER_LOW, ACT_COVER_AR2_LOW, true }, { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR2_LOW, false }, { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_AR2_LOW, false }, - { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_AR2_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR2, true }, // { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, #else @@ -160,6 +156,21 @@ acttable_t CWeaponAR2::m_acttable[] = { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, // { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, #endif + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_AR2_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_AR2_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponAR2); diff --git a/sp/src/game/server/hl2/weapon_citizenpackage.cpp b/sp/src/game/server/hl2/weapon_citizenpackage.cpp index cdb9378a..3d82e46c 100644 --- a/sp/src/game/server/hl2/weapon_citizenpackage.cpp +++ b/sp/src/game/server/hl2/weapon_citizenpackage.cpp @@ -23,6 +23,13 @@ acttable_t CWeaponCitizenPackage::m_acttable[] = { { ACT_IDLE, ACT_IDLE_PACKAGE, false }, { ACT_WALK, ACT_WALK_PACKAGE, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_PACKAGE, false }, + + { ACT_IDLE_ANGRY, ACT_IDLE_PACKAGE, false }, + { ACT_WALK_AIM, ACT_WALK_PACKAGE, false }, + { ACT_RUN_AIM, ACT_RUN_PACKAGE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCitizenPackage); @@ -70,5 +77,12 @@ acttable_t CWeaponCitizenSuitcase::m_acttable[] = { { ACT_IDLE, ACT_IDLE_SUITCASE, false }, { ACT_WALK, ACT_WALK_SUITCASE, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_SUITCASE, false }, + + { ACT_IDLE_ANGRY, ACT_IDLE_SUITCASE, false }, + { ACT_WALK_AIM, ACT_WALK_SUITCASE, false }, + { ACT_RUN_AIM, ACT_RUN_SUITCASE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCitizenSuitcase); diff --git a/sp/src/game/server/hl2/weapon_crossbow.cpp b/sp/src/game/server/hl2/weapon_crossbow.cpp index b86b4c05..1ce3329c 100644 --- a/sp/src/game/server/hl2/weapon_crossbow.cpp +++ b/sp/src/game/server/hl2/weapon_crossbow.cpp @@ -542,7 +542,7 @@ public: virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); virtual bool Reload( void ); #ifdef MAPBASE - virtual void Reload_NPC( void ); + virtual void Reload_NPC( bool bPlaySound = true ); #endif virtual void ItemPostFrame( void ); virtual void ItemBusyFrame( void ); @@ -638,6 +638,15 @@ END_DATADESC() #ifdef MAPBASE acttable_t CWeaponCrossbow::m_acttable[] = { +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_CROSSBOW, true }, + { ACT_RELOAD, ACT_RELOAD_CROSSBOW, true }, + { ACT_IDLE, ACT_IDLE_CROSSBOW, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_CROSSBOW, true }, + + { ACT_WALK, ACT_WALK_CROSSBOW, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_CROSSBOW, true }, +#else { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true }, { ACT_RELOAD, ACT_RELOAD_SMG1, true }, { ACT_IDLE, ACT_IDLE_SMG1, true }, @@ -645,6 +654,7 @@ acttable_t CWeaponCrossbow::m_acttable[] = { ACT_WALK, ACT_WALK_RIFLE, true }, { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true }, +#endif // Readiness activities (not aiming) { ACT_IDLE_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims @@ -673,6 +683,24 @@ acttable_t CWeaponCrossbow::m_acttable[] = { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims //End readiness activities +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_AIM, ACT_WALK_AIM_CROSSBOW, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_CROSSBOW, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_CROSSBOW, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_CROSSBOW, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_CROSSBOW_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_CROSSBOW_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_CROSSBOW_LOW, false }, + { ACT_RELOAD_LOW, ACT_RELOAD_CROSSBOW_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_CROSSBOW, true }, + + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#else { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true }, { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, @@ -686,6 +714,12 @@ acttable_t CWeaponCrossbow::m_acttable[] = { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false }, { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_CROSSBOW_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_CROSSBOW_MED, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCrossbow); @@ -785,11 +819,15 @@ bool CWeaponCrossbow::Reload( void ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CWeaponCrossbow::Reload_NPC( void ) +void CWeaponCrossbow::Reload_NPC( bool bPlaySound ) { - BaseClass::Reload_NPC(); + BaseClass::Reload_NPC( bPlaySound ); - m_nSkin = 0; + int iBody = FindBodygroupByName( "bolt" ); + if (iBody != -1) + SetBodygroup( iBody, 0 ); + else + m_nSkin = 0; } #endif @@ -939,7 +977,11 @@ void CWeaponCrossbow::FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin, m_iClip1--; - m_nSkin = 1; + int iBody = FindBodygroupByName( "bolt" ); + if (iBody != -1) + SetBodygroup( iBody, 1 ); + else + m_nSkin = 1; WeaponSound( SINGLE_NPC ); WeaponSound( SPECIAL2 ); diff --git a/sp/src/game/server/hl2/weapon_crowbar.cpp b/sp/src/game/server/hl2/weapon_crowbar.cpp index fdbc7943..8a0daacf 100644 --- a/sp/src/game/server/hl2/weapon_crowbar.cpp +++ b/sp/src/game/server/hl2/weapon_crowbar.cpp @@ -42,6 +42,13 @@ acttable_t CWeaponCrowbar::m_acttable[] = { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, { ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RUN, ACT_RUN_MELEE, false }, + { ACT_WALK, ACT_WALK_MELEE, false }, + + { ACT_ARM, ACT_ARM_MELEE, false }, + { ACT_DISARM, ACT_DISARM_MELEE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponCrowbar); diff --git a/sp/src/game/server/hl2/weapon_crowbar.h b/sp/src/game/server/hl2/weapon_crowbar.h index 1890ef6e..26bb961f 100644 --- a/sp/src/game/server/hl2/weapon_crowbar.h +++ b/sp/src/game/server/hl2/weapon_crowbar.h @@ -46,6 +46,12 @@ public: // Animation event virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + // Don't use backup activities + acttable_t *GetBackupActivityList() { return NULL; } + int GetBackupActivityListCount() { return 0; } +#endif + private: // Animation event handlers void HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); diff --git a/sp/src/game/server/hl2/weapon_pistol.cpp b/sp/src/game/server/hl2/weapon_pistol.cpp index 9a571bf5..a04d30b6 100644 --- a/sp/src/game/server/hl2/weapon_pistol.cpp +++ b/sp/src/game/server/hl2/weapon_pistol.cpp @@ -108,6 +108,12 @@ public: return 0.5f; } +#ifdef MAPBASE + // Pistols are their own backup activities + virtual acttable_t *GetBackupActivityList() { return NULL; } + virtual int GetBackupActivityListCount() { return 0; } +#endif + DECLARE_ACTTABLE(); private: @@ -155,6 +161,40 @@ acttable_t CWeaponPistol::m_acttable[] = // Activities ported from weapon_alyxgun below // +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_PISTOL_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_PISTOL_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_STEALTH, ACT_WALK_STEALTH_PISTOL, false }, + + { ACT_RUN_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_PISTOL_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_STEALTH, ACT_RUN_STEALTH_PISTOL, false }, + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_PISTOL_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_AIM_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_AIM_RELAXED, ACT_WALK_PISTOL_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_PISTOL, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_PISTOL, false },//always aims + { ACT_WALK_AIM_STEALTH, ACT_WALK_AIM_STEALTH_PISTOL, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_PISTOL_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_PISTOL, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims + { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims + //End readiness activities +#else // Readiness activities (not aiming) { ACT_IDLE_RELAXED, ACT_IDLE_PISTOL, false },//never aims { ACT_IDLE_STIMULATED, ACT_IDLE_STIMULATED, false }, @@ -187,6 +227,7 @@ acttable_t CWeaponPistol::m_acttable[] = { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_PISTOL, false },//always aims { ACT_RUN_AIM_STEALTH, ACT_RUN_AIM_STEALTH_PISTOL, false },//always aims //End readiness activities +#endif // Crouch activities { ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false }, @@ -199,11 +240,41 @@ acttable_t CWeaponPistol::m_acttable[] = { ACT_READINESS_AGITATED_TO_STIMULATED, ACT_READINESS_PISTOL_AGITATED_TO_STIMULATED, false }, { ACT_READINESS_STIMULATED_TO_RELAXED, ACT_READINESS_PISTOL_STIMULATED_TO_RELAXED, false }, #endif + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_PISTOL, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_PISTOL, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_PISTOL, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_PISTOL, true }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_PISTOL_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_PISTOL_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_PISTOL, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_PISTOL, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_PISTOL, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_PISTOL, false }, +#endif }; IMPLEMENT_ACTTABLE( CWeaponPistol ); +#ifdef MAPBASE +// Allows Weapon_BackupActivity() to access the pistol's activity table. +acttable_t *GetPistolActtable() +{ + return CWeaponPistol::m_acttable; +} + +int GetPistolActtableCount() +{ + return ARRAYSIZE(CWeaponPistol::m_acttable); +} +#endif + //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- diff --git a/sp/src/game/server/hl2/weapon_rpg.cpp b/sp/src/game/server/hl2/weapon_rpg.cpp index 3ae41e28..2d20211d 100644 --- a/sp/src/game/server/hl2/weapon_rpg.cpp +++ b/sp/src/game/server/hl2/weapon_rpg.cpp @@ -1399,6 +1399,11 @@ PRECACHE_WEAPON_REGISTER(weapon_rpg); acttable_t CWeaponRPG::m_acttable[] = { { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_RPG_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_RPG_LOW, false }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_RPG, false }, +#endif { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true }, { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true }, @@ -1411,6 +1416,16 @@ acttable_t CWeaponRPG::m_acttable[] = { ACT_RUN, ACT_RUN_RPG, true }, { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true }, { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true }, + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RPG, false }, + { ACT_DISARM, ACT_DISARM_RPG, false }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_RPG_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_RPG_MED, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponRPG); diff --git a/sp/src/game/server/hl2/weapon_shotgun.cpp b/sp/src/game/server/hl2/weapon_shotgun.cpp index fb9ec215..e0b2e336 100644 --- a/sp/src/game/server/hl2/weapon_shotgun.cpp +++ b/sp/src/game/server/hl2/weapon_shotgun.cpp @@ -108,6 +108,57 @@ END_DATADESC() acttable_t CWeaponShotgun::m_acttable[] = { +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Note that ACT_IDLE_SHOTGUN_AGITATED seems to be a stand-in for ACT_IDLE_SHOTGUN on citizens, + // but that isn't acceptable for NPCs which don't use readiness activities. + { ACT_IDLE, ACT_IDLE_SHOTGUN, true }, + + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true }, + { ACT_RELOAD, ACT_RELOAD_SHOTGUN, false }, + { ACT_WALK, ACT_WALK_SHOTGUN, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SHOTGUN, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SHOTGUN_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_SHOTGUN_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_SHOTGUN_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_SHOTGUN, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_SHOTGUN_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_SHOTGUN_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_SHOTGUN, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SHOTGUN_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_SHOTGUN_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SHOTGUN, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_SHOTGUN_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_SHOTGUN_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_SHOTGUN, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_SHOTGUN_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_SHOTGUN_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_SHOTGUN, false },//always aims +//End readiness activities + + { ACT_WALK_AIM, ACT_WALK_AIM_SHOTGUN, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RIFLE, true }, + { ACT_WALK_CROUCH_AIM, ACT_WALK_CROUCH_AIM_RIFLE, true }, + { ACT_RUN, ACT_RUN_SHOTGUN, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_SHOTGUN, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RIFLE, true }, + { ACT_RUN_CROUCH_AIM, ACT_RUN_CROUCH_AIM_RIFLE, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_SHOTGUN, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true }, + { ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false }, + { ACT_COVER_LOW, ACT_COVER_SHOTGUN_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SHOTGUN_LOW, false }, +#else { ACT_IDLE, ACT_IDLE_SMG1, true }, // FIXME: hook to shotgun unique { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, true }, @@ -153,6 +204,17 @@ acttable_t CWeaponShotgun::m_acttable[] = { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SHOTGUN_LOW, true }, { ACT_RELOAD_LOW, ACT_RELOAD_SHOTGUN_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false }, +#endif + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_SHOTGUN, true }, + { ACT_DISARM, ACT_DISARM_SHOTGUN, true }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SHOTGUN_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SHOTGUN_MED, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponShotgun); diff --git a/sp/src/game/server/hl2/weapon_smg1.cpp b/sp/src/game/server/hl2/weapon_smg1.cpp index 97b0e89e..04f7e390 100644 --- a/sp/src/game/server/hl2/weapon_smg1.cpp +++ b/sp/src/game/server/hl2/weapon_smg1.cpp @@ -134,6 +134,21 @@ acttable_t CWeaponSMG1::m_acttable[] = { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_SMG1_LOW, false }, { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, + +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + { ACT_RANGE_AIM_MED, ACT_RANGE_AIM_SMG1_MED, false }, + { ACT_RANGE_ATTACK1_MED, ACT_RANGE_ATTACK_SMG1_MED, false }, + + { ACT_COVER_WALL_R, ACT_COVER_WALL_R_RIFLE, false }, + { ACT_COVER_WALL_L, ACT_COVER_WALL_L_RIFLE, false }, + { ACT_COVER_WALL_LOW_R, ACT_COVER_WALL_LOW_R_RIFLE, false }, + { ACT_COVER_WALL_LOW_L, ACT_COVER_WALL_LOW_L_RIFLE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponSMG1); diff --git a/sp/src/game/server/mapbase/ai_grenade.h b/sp/src/game/server/mapbase/ai_grenade.h index 69031d74..bfcbd4de 100644 --- a/sp/src/game/server/mapbase/ai_grenade.h +++ b/sp/src/game/server/mapbase/ai_grenade.h @@ -41,6 +41,7 @@ DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), \ DEFINE_FIELD( m_iLastAnimEventHandled, FIELD_INTEGER ), \ DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeAtTarget", InputThrowGrenadeAtTarget ), \ + DEFINE_INPUTFUNC( FIELD_STRING, "ThrowGrenadeGestureAtTarget", InputThrowGrenadeGestureAtTarget ), \ DEFINE_INPUTFUNC( FIELD_INTEGER, "SetGrenades", InputSetGrenades ), \ DEFINE_INPUTFUNC( FIELD_INTEGER, "AddGrenades", InputAddGrenades ), \ DEFINE_OUTPUT(m_OnThrowGrenade, "OnThrowGrenade"), \ @@ -124,6 +125,7 @@ public: void InputSetGrenades( inputdata_t &inputdata ) { AddGrenades( inputdata.value.Int() - m_iNumGrenades ); } void InputAddGrenades( inputdata_t &inputdata ) { AddGrenades( inputdata.value.Int() ); } void InputThrowGrenadeAtTarget( inputdata_t &inputdata ); + void InputThrowGrenadeGestureAtTarget( inputdata_t &inputdata ); virtual void DelayGrenadeCheck( float delay ) { m_flNextGrenadeCheck = gpGlobals->curtime + delay; } @@ -294,6 +296,63 @@ void CAI_GrenadeUser::InputThrowGrenadeAtTarget( inputdata_t &inputdat this->ClearSchedule( "Told to throw grenade via input" ); } +//----------------------------------------------------------------------------- +// Purpose: Force the combine soldier to throw a grenade at the target using the gesture animation. +// If I'm a combine elite, fire my combine ball at the target instead. +// Input : &inputdata - +//----------------------------------------------------------------------------- +template +void CAI_GrenadeUser::InputThrowGrenadeGestureAtTarget( inputdata_t &inputdata ) +{ + // Ignore if we're inside a scripted sequence + //if ( this->GetState() == NPC_STATE_SCRIPT && this->m_hCine ) + // return; + + CBaseEntity *pEntity = gEntList.FindEntityByName( NULL, inputdata.value.String(), this, inputdata.pActivator, inputdata.pCaller ); + if ( !pEntity ) + { + DevMsg("%s (%s) received ThrowGrenadeGestureAtTarget input, but couldn't find target entity '%s'\n", this->GetClassname(), this->GetDebugName(), inputdata.value.String() ); + return; + } + + m_hForcedGrenadeTarget = pEntity; + m_flNextGrenadeCheck = 0; + + Vector vecTarget = m_hForcedGrenadeTarget->WorldSpaceCenter(); + +#ifdef SHARED_COMBINE_ACTIVITIES + if (IsAltFireCapable()) + { + if (FVisible( m_hForcedGrenadeTarget )) + { + m_vecAltFireTarget = vecTarget; + m_hForcedGrenadeTarget = NULL; + + int iLayer = AddGesture( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + if (iLayer != -1) + { + this->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + this->GetLayerDuration( iLayer ) ); + } + } + } + else + { + // If we can, throw a grenade at the target. + // Ignore grenade count / distance / etc + if (CheckCanThrowGrenade( vecTarget )) + { + int iLayer = AddGesture( ACT_GESTURE_COMBINE_THROW_GRENADE ); + if (iLayer != -1) + { + this->GetShotRegulator()->FireNoEarlierThan( gpGlobals->curtime + this->GetLayerDuration( iLayer ) ); + } + } + } +#else + Warning("Gesture grenades/alt-fire not supported\n"); +#endif +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- template diff --git a/sp/src/game/server/props.cpp b/sp/src/game/server/props.cpp index 9263628b..2affea8a 100644 --- a/sp/src/game/server/props.cpp +++ b/sp/src/game/server/props.cpp @@ -4099,6 +4099,11 @@ BEGIN_DATADESC(CBasePropDoor) DEFINE_KEYFIELD(m_SoundClose, FIELD_SOUNDNAME, "soundcloseoverride"), DEFINE_KEYFIELD(m_ls.sLockedSound, FIELD_SOUNDNAME, "soundlockedoverride"), DEFINE_KEYFIELD(m_ls.sUnlockedSound, FIELD_SOUNDNAME, "soundunlockedoverride"), +#ifdef MAPBASE + DEFINE_KEYFIELD(m_flNPCOpenDistance, FIELD_FLOAT, "opendistoverride"), + DEFINE_KEYFIELD(m_eNPCOpenFrontActivity, FIELD_INTEGER, "openfrontactivityoverride"), + DEFINE_KEYFIELD(m_eNPCOpenBackActivity, FIELD_INTEGER, "openbackactivityoverride"), +#endif DEFINE_KEYFIELD(m_SlaveName, FIELD_STRING, "slavename" ), DEFINE_FIELD(m_bLocked, FIELD_BOOLEAN), //DEFINE_KEYFIELD(m_flBlockDamage, FIELD_FLOAT, "dmg"), @@ -4143,6 +4148,11 @@ END_SEND_TABLE() CBasePropDoor::CBasePropDoor( void ) { m_hMaster = NULL; +#ifdef MAPBASE + m_flNPCOpenDistance = -1; + m_eNPCOpenFrontActivity = ACT_INVALID; + m_eNPCOpenBackActivity = ACT_INVALID; +#endif } //----------------------------------------------------------------------------- @@ -4340,6 +4350,32 @@ void CBasePropDoor::CalcDoorSounds() { strSoundLocked = AllocPooledString( pkvHardwareData->GetString( "locked" ) ); strSoundUnlocked = AllocPooledString( pkvHardwareData->GetString( "unlocked" ) ); + +#ifdef MAPBASE + if (m_eNPCOpenFrontActivity == ACT_INVALID) + { + const char *pszActivity = pkvHardwareData->GetString( "activity_front" ); + if (pszActivity[0] != '\0') + { + m_eNPCOpenFrontActivity = (Activity)CAI_BaseNPC::GetActivityID( pszActivity ); + if (m_eNPCOpenFrontActivity == ACT_INVALID) + m_eNPCOpenFrontActivity = ActivityList_RegisterPrivateActivity( pszActivity ); + } + } + if (m_eNPCOpenBackActivity == ACT_INVALID) + { + const char *pszActivity = pkvHardwareData->GetString( "activity_back" ); + if (pszActivity[0] != '\0') + { + m_eNPCOpenBackActivity = (Activity)CAI_BaseNPC::GetActivityID( pszActivity ); + if (m_eNPCOpenBackActivity == ACT_INVALID) + m_eNPCOpenBackActivity = ActivityList_RegisterPrivateActivity( pszActivity ); + } + } + + if (m_flNPCOpenDistance == -1) + m_flNPCOpenDistance = pkvHardwareData->GetFloat( "npc_distance", 32.0 ); +#endif } // If any sounds were missing, try the "defaults" block. @@ -5940,6 +5976,11 @@ void CPropDoorRotating::DoorResume( void ) AngularMove( m_angGoal, m_flSpeed ); } +#ifdef MAPBASE +ConVar ai_door_enable_acts( "ai_door_enable_acts", "1", FCVAR_NONE, "Enables the new door-opening activities." ); +ConVar ai_door_open_dist_override( "ai_door_open_dist_override", "-1", FCVAR_NONE, "Overrides the distance from a door a NPC has to navigate to in order to open a door." ); +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : vecMoveDir - @@ -5961,20 +6002,37 @@ void CPropDoorRotating::GetNPCOpenData(CAI_BaseNPC *pNPC, opendata_t &opendata) Vector vecNPCOrigin = pNPC->GetAbsOrigin(); +#ifdef MAPBASE + float flPosOffset = ai_door_open_dist_override.GetFloat() >= 0.0f ? ai_door_open_dist_override.GetFloat() : GetNPCOpenDistance(); +#else + float flPosOffset = 64; +#endif + if (pNPC->GetAbsOrigin().Dot(vecForward) > GetAbsOrigin().Dot(vecForward)) { // In front of the door relative to the door's forward vector. - opendata.vecStandPos += vecForward * 64; + opendata.vecStandPos += vecForward * flPosOffset; opendata.vecFaceDir = -vecForward; +#ifdef MAPBASE + opendata.eActivity = !ai_door_enable_acts.GetBool() ? ACT_INVALID : GetNPCOpenFrontActivity(); +#endif } else { // Behind the door relative to the door's forward vector. - opendata.vecStandPos -= vecForward * 64; + opendata.vecStandPos -= vecForward * flPosOffset; opendata.vecFaceDir = vecForward; +#ifdef MAPBASE + opendata.eActivity = !ai_door_enable_acts.GetBool() ? ACT_INVALID : GetNPCOpenBackActivity(); +#endif } +#ifdef MAPBASE + if (opendata.eActivity == ACT_INVALID) + opendata.eActivity = ACT_OPEN_DOOR; +#else opendata.eActivity = ACT_OPEN_DOOR; +#endif } diff --git a/sp/src/game/shared/activitylist.cpp b/sp/src/game/shared/activitylist.cpp index 9e986cb7..ecee2eee 100644 --- a/sp/src/game/shared/activitylist.cpp +++ b/sp/src/game/shared/activitylist.cpp @@ -2296,15 +2296,22 @@ void ActivityList_RegisterSharedActivities( void ) REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_AR2 ); REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2 ); - //REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_AR2_LOW ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_AR2 ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_AR2_LOW ); #endif #ifdef SHARED_COMBINE_ACTIVITIES REGISTER_SHARED_ACTIVITY( ACT_COMBINE_THROW_GRENADE ); REGISTER_SHARED_ACTIVITY( ACT_COMBINE_AR2_ALTFIRE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_THROW_GRENADE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_COMBINE_AR2_ALTFIRE ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK1 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SPECIAL_ATTACK2 ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_ADVANCE ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_FORWARD ); REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_GROUP ); @@ -2314,9 +2321,133 @@ void ActivityList_RegisterSharedActivities( void ) REGISTER_SHARED_ACTIVITY( ACT_GESTURE_SIGNAL_TAKECOVER ); #endif -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_IDLE_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_REVOLVER ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_REVOLVER ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_ANGRY_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RELOAD_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_CROSSBOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RELOAD_CROSSBOW ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_IDLE_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_PISTOL_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_PISTOL_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_PISTOL_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_CROUCH_AIM_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_CROUCH_AIM_PISTOL ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_SHOTGUN_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_LOW ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_RELAXED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SHOTGUN_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_IDLE_AIM_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_WALK_AIM_SHOTGUN_STIMULATED ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_AIM_SHOTGUN_STIMULATED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_LOW ); + REGISTER_SHARED_ACTIVITY( ACT_GESTURE_RANGE_ATTACK_RPG ); + + REGISTER_SHARED_ACTIVITY( ACT_WALK_MELEE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_MELEE ); + + REGISTER_SHARED_ACTIVITY( ACT_RUN_PACKAGE ); + REGISTER_SHARED_ACTIVITY( ACT_RUN_SUITCASE ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_ARM_MELEE ); REGISTER_SHARED_ACTIVITY( ACT_DISARM_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_SHOTGUN ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_RPG ); + REGISTER_SHARED_ACTIVITY( ACT_DISARM_MELEE ); +#endif + +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_ALL ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_IDLE ); + + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_TOP ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_MOUNT_BOTTOM ); + REGISTER_SHARED_ACTIVITY( ACT_CLIMB_DISMOUNT_BOTTOM ); +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_AR2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_SHOTGUN_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_PISTOL_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_RPG_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_REVOLVER_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_ATTACK_CROSSBOW_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_AR2_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SMG1_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_SHOTGUN_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_PISTOL_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_RPG_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_REVOLVER_MED ); + REGISTER_SHARED_ACTIVITY( ACT_RANGE_AIM_CROSSBOW_MED ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_RIFLE ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_RIFLE ); + + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_R_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_L_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_R_PISTOL ); + REGISTER_SHARED_ACTIVITY( ACT_COVER_WALL_LOW_L_PISTOL ); #endif AssertMsg( g_HighestActivity == LAST_SHARED_ACTIVITY - 1, "Not all activities from ai_activity.h registered in activitylist.cpp" ); diff --git a/sp/src/game/shared/ai_activity.h b/sp/src/game/shared/ai_activity.h index 44b8f62d..ad39c548 100644 --- a/sp/src/game/shared/ai_activity.h +++ b/sp/src/game/shared/ai_activity.h @@ -13,42 +13,42 @@ #ifdef MAPBASE -// Mapbase adds a few shared activities. // -// These used to be placed in between existing activities, as outside of the code activities are based off of strings. -// This seemed like a bad idea, but no problems arose at the time. -// I later discovered that apparently some things in MP use the direct integers instead of the enum names. -// I retroactively put all custom activities at the bottom of the enum instead. -// Their placements in activitylist.cpp and ai_activity.cpp have not been changed. +// Mapbase adds many new shared activities, primarily for NPCs. +// +// These are at the bottom of the enum to prevent disruptions in the order of existing activities. +// // AR2 ACTIVITY FIX -// You know all of those AR2 activities that citizens and combine soldiers have? -// Yeah, those are unused. It appears Valve forgot to implement them. +// Citizens and Combine soldiers have several activities for the SMG1 and AR2 which differ from each other. +// Across both NPCs, there are around 20-40 different AR2 animations. Most of these animations are similar to the +// SMG1 animations, except their hand positions are adjusted to use the AR2 instead. // -// What could be 20-40 different animations on two different characters are not even defined in code. -// I didn't even realize they were unused until I saw ACT_RELOAD_AR2, so I don't blame them for never realizing this. -// They work surprisingly well for probably never being tested in-game. +// Unfortunately, the vast majority of the AR2 animations in those models are not declared as real activities in +// code. The AR2 instead falls back to its SMG1 animation counterparts. +// This is thought to be an oversight which dates back to late in Half-Life 2's development. // -// 1 = Add activities directly -// 2 = Add activities as custom activities (todo) -// -// 2 should only be preferable if adding them like this breaks something. +// This preprocessor declares those activities and implements them on the AR2. In-game, they work surprisingly well +// despite presumably never being tested in-game during HL2's development. #define AR2_ACTIVITY_FIX 1 -// COMPANION HOLSTER WORKAROUND -// I introduced a separate holster/unholster animation to male_shared -// and female_shared and I realized it might conflict with Alyx's animation. -// -// I came up with a solution--ACT_ARM_RIFLE and its disarm counterpart--to solve it. -// I didn't think about the fact I could've named them the same as Alyx's so her animations would overwrite it... -// ...so this has been deactivated. -//#define COMPANION_HOLSTER_WORKAROUND 1 - // SHARED COMBINE ACTIVITIES -// This turns ACT_COMBINE_AR2_ALTFIRE and ACT_COMBINE_THROW_GRENADE into shared activities. -// This is necessary so other NPCs to use them without having to rely on a bunch of custom activities. +// This turns ACT_COMBINE_AR2_ALTFIRE, ACT_COMBINE_THROW_GRENADE, and their new gesture counterparts into shared activities. +// This is necessary for other NPCs to use them without having to rely on private custom activities declared through the AI definition system. #define SHARED_COMBINE_ACTIVITIES 1 +// EXPANDED HL2 WEAPON ACTIVITIES +// This enables a bunch of new activities for Half-Life 2 weapons, including new 357 animations and readiness activities for pistols. +#define EXPANDED_HL2_WEAPON_ACTIVITIES 1 + +// EXPANDED NAVIGATION ACTIVITIES +// This enables some new navigation-related activities. +#define EXPANDED_NAVIGATION_ACTIVITIES 1 + +// EXPANDED HL2 COVER ACTIVITIES +// This enables some new cover-related activities. +#define EXPANDED_HL2_COVER_ACTIVITIES 1 + #endif #define ACTIVITY_NOT_AVAILABLE -1 @@ -2169,16 +2169,23 @@ typedef enum ACT_RUN_AIM_AR2, ACT_RELOAD_AR2, - //ACT_RELOAD_AR2_LOW, + ACT_RELOAD_AR2_LOW, ACT_GESTURE_RELOAD_AR2, + + ACT_COVER_AR2_LOW, #endif #ifdef SHARED_COMBINE_ACTIVITIES ACT_COMBINE_THROW_GRENADE, ACT_COMBINE_AR2_ALTFIRE, - // New gesture-based signals as activities for people who want to use them + // Gesture versions for existing Combine signal and grenade activities + ACT_GESTURE_COMBINE_THROW_GRENADE, + ACT_GESTURE_COMBINE_AR2_ALTFIRE, + ACT_GESTURE_SPECIAL_ATTACK1, + ACT_GESTURE_SPECIAL_ATTACK2, + ACT_GESTURE_SIGNAL_ADVANCE, ACT_GESTURE_SIGNAL_FORWARD, ACT_GESTURE_SIGNAL_GROUP, @@ -2188,9 +2195,143 @@ typedef enum ACT_GESTURE_SIGNAL_TAKECOVER, #endif -#ifdef COMPANION_HOLSTER_WORKAROUND +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + // Revolver (357) + ACT_IDLE_REVOLVER, + ACT_IDLE_ANGRY_REVOLVER, + ACT_WALK_REVOLVER, + ACT_RUN_REVOLVER, + ACT_WALK_AIM_REVOLVER, + ACT_RUN_AIM_REVOLVER, + ACT_RANGE_ATTACK_REVOLVER, + ACT_RELOAD_REVOLVER, + ACT_RANGE_ATTACK_REVOLVER_LOW, + ACT_RELOAD_REVOLVER_LOW, + ACT_COVER_REVOLVER_LOW, + ACT_RANGE_AIM_REVOLVER_LOW, + ACT_GESTURE_RANGE_ATTACK_REVOLVER, + ACT_GESTURE_RELOAD_REVOLVER, + + // Crossbow + ACT_IDLE_CROSSBOW, + ACT_IDLE_ANGRY_CROSSBOW, + ACT_WALK_CROSSBOW, + ACT_RUN_CROSSBOW, + ACT_WALK_AIM_CROSSBOW, + ACT_RUN_AIM_CROSSBOW, + ACT_RANGE_ATTACK_CROSSBOW, + ACT_RELOAD_CROSSBOW, + ACT_RANGE_ATTACK_CROSSBOW_LOW, + ACT_RELOAD_CROSSBOW_LOW, + ACT_COVER_CROSSBOW_LOW, + ACT_RANGE_AIM_CROSSBOW_LOW, + ACT_GESTURE_RANGE_ATTACK_CROSSBOW, + ACT_GESTURE_RELOAD_CROSSBOW, + + // Pistol + ACT_IDLE_PISTOL_RELAXED, + ACT_IDLE_PISTOL_STIMULATED, + ACT_WALK_PISTOL_RELAXED, + ACT_WALK_PISTOL_STIMULATED, + ACT_RUN_PISTOL_RELAXED, + ACT_RUN_PISTOL_STIMULATED, + + ACT_IDLE_AIM_PISTOL_STIMULATED, + ACT_WALK_AIM_PISTOL_STIMULATED, + ACT_RUN_AIM_PISTOL_STIMULATED, + + ACT_WALK_CROUCH_PISTOL, + ACT_WALK_CROUCH_AIM_PISTOL, + ACT_RUN_CROUCH_PISTOL, + ACT_RUN_CROUCH_AIM_PISTOL, + + // Shotgun + ACT_IDLE_SHOTGUN, + ACT_WALK_SHOTGUN, + ACT_RUN_SHOTGUN, + + ACT_COVER_SHOTGUN_LOW, + ACT_RANGE_AIM_SHOTGUN_LOW, + + ACT_WALK_SHOTGUN_RELAXED, + ACT_WALK_SHOTGUN_STIMULATED, + ACT_RUN_SHOTGUN_RELAXED, + ACT_RUN_SHOTGUN_STIMULATED, + + ACT_IDLE_AIM_SHOTGUN_STIMULATED, + ACT_WALK_AIM_SHOTGUN_STIMULATED, + ACT_RUN_AIM_SHOTGUN_STIMULATED, + + // RPG + ACT_RANGE_AIM_RPG_LOW, + ACT_RANGE_ATTACK_RPG_LOW, + ACT_GESTURE_RANGE_ATTACK_RPG, + + // Melee + ACT_WALK_MELEE, + ACT_RUN_MELEE, + + // Citizen accessories + ACT_RUN_PACKAGE, + ACT_RUN_SUITCASE, + + // Holster/Unholster ACT_ARM_RIFLE, + ACT_ARM_SHOTGUN, + ACT_ARM_RPG, + ACT_ARM_MELEE, ACT_DISARM_RIFLE, + ACT_DISARM_SHOTGUN, + ACT_DISARM_RPG, + ACT_DISARM_MELEE, +#endif + +#ifdef EXPANDED_NAVIGATION_ACTIVITIES + ACT_CLIMB_ALL, // An actual blend animation which uses pose parameters for direction + ACT_CLIMB_IDLE, + + ACT_CLIMB_MOUNT_TOP, + ACT_CLIMB_MOUNT_BOTTOM, + ACT_CLIMB_DISMOUNT_BOTTOM, +#endif + +#ifdef EXPANDED_HL2_COVER_ACTIVITIES + // Crouch Cover Medium + ACT_RANGE_ATTACK1_MED, + ACT_RANGE_ATTACK2_MED, + ACT_RANGE_AIM_MED, + + ACT_RANGE_ATTACK_AR2_MED, + ACT_RANGE_ATTACK_SMG1_MED, + ACT_RANGE_ATTACK_SHOTGUN_MED, + ACT_RANGE_ATTACK_PISTOL_MED, + ACT_RANGE_ATTACK_RPG_MED, + ACT_RANGE_ATTACK_REVOLVER_MED, + ACT_RANGE_ATTACK_CROSSBOW_MED, + + ACT_RANGE_AIM_AR2_MED, + ACT_RANGE_AIM_SMG1_MED, + ACT_RANGE_AIM_SHOTGUN_MED, + ACT_RANGE_AIM_PISTOL_MED, + ACT_RANGE_AIM_RPG_MED, + ACT_RANGE_AIM_REVOLVER_MED, + ACT_RANGE_AIM_CROSSBOW_MED, + + // Wall Cover (for use in custom cover hints) + ACT_COVER_WALL_R, + ACT_COVER_WALL_L, + ACT_COVER_WALL_LOW_R, + ACT_COVER_WALL_LOW_L, + + ACT_COVER_WALL_R_RIFLE, + ACT_COVER_WALL_L_RIFLE, + ACT_COVER_WALL_LOW_R_RIFLE, + ACT_COVER_WALL_LOW_L_RIFLE, + + ACT_COVER_WALL_R_PISTOL, + ACT_COVER_WALL_L_PISTOL, + ACT_COVER_WALL_LOW_R_PISTOL, + ACT_COVER_WALL_LOW_L_PISTOL, #endif // this is the end of the global activities, private per-monster activities start here. diff --git a/sp/src/game/shared/basecombatweapon_shared.cpp b/sp/src/game/shared/basecombatweapon_shared.cpp index a48aa413..3e23ab0f 100644 --- a/sp/src/game/shared/basecombatweapon_shared.cpp +++ b/sp/src/game/shared/basecombatweapon_shared.cpp @@ -1046,7 +1046,13 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassify() Activity idleact = ActivityOverride(ACT_IDLE_ANGRY, NULL); switch (idleact) { +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_REVOLVER: +#endif case ACT_IDLE_ANGRY_PISTOL: return WEPCLASS_HANDGUN; +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + case ACT_IDLE_ANGRY_CROSSBOW: // For now, crossbows are rifles +#endif case ACT_IDLE_ANGRY_SMG1: case ACT_IDLE_ANGRY_AR2: return WEPCLASS_RIFLE; case ACT_IDLE_ANGRY_SHOTGUN: return WEPCLASS_SHOTGUN; @@ -1077,6 +1083,11 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassFromString(const char *str) return WEPCLASS_INVALID; } +#ifdef HL2_DLL +extern acttable_t *GetSMG1Acttable(); +extern int GetSMG1ActtableCount(); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1084,12 +1095,32 @@ bool CBaseCombatWeapon::SupportsBackupActivity(Activity activity) { // Derived classes should override this. - // Pistol and melee users should not use SMG animations for missing pistol activities. - if (WeaponClassify() == WEPCLASS_HANDGUN || IsMeleeWeapon()) +#ifdef HL2_DLL + // Melee users should not use SMG animations for missing activities. + if (IsMeleeWeapon() && GetBackupActivityList() == GetSMG1Acttable()) return false; +#endif return true; } + +acttable_t *CBaseCombatWeapon::GetBackupActivityList() +{ +#ifdef HL2_DLL + return GetSMG1Acttable(); +#else + return NULL; +#endif +} + +int CBaseCombatWeapon::GetBackupActivityListCount() +{ +#ifdef HL2_DLL + return GetSMG1ActtableCount(); +#else + return 0; +#endif +} #endif @@ -2343,9 +2374,10 @@ bool CBaseCombatWeapon::Reload( void ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CBaseCombatWeapon::Reload_NPC( void ) +void CBaseCombatWeapon::Reload_NPC( bool bPlaySound ) { - WeaponSound( RELOAD_NPC ); + if (bPlaySound) + WeaponSound( RELOAD_NPC ); if (UsesClipsForAmmo1()) { diff --git a/sp/src/game/shared/basecombatweapon_shared.h b/sp/src/game/shared/basecombatweapon_shared.h index b0a3e330..abfdb3b0 100644 --- a/sp/src/game/shared/basecombatweapon_shared.h +++ b/sp/src/game/shared/basecombatweapon_shared.h @@ -230,6 +230,8 @@ public: static WeaponClass_t WeaponClassFromString(const char *str); virtual bool SupportsBackupActivity(Activity activity); + virtual acttable_t *GetBackupActivityList(); + virtual int GetBackupActivityListCount(); #endif virtual void Equip( CBaseCombatCharacter *pOwner ); @@ -319,7 +321,7 @@ public: bool ReloadsSingly( void ) const; #ifdef MAPBASE // Originally created for the crossbow, can be used to add special NPC reloading behavior - virtual void Reload_NPC( void ); + virtual void Reload_NPC( bool bPlaySound = true ); #endif virtual bool AutoFiresFullClip( void ) { return false; } diff --git a/sp/src/game/shared/hl2mp/weapon_stunstick.cpp b/sp/src/game/shared/hl2mp/weapon_stunstick.cpp index 1d68b529..992bd036 100644 --- a/sp/src/game/shared/hl2mp/weapon_stunstick.cpp +++ b/sp/src/game/shared/hl2mp/weapon_stunstick.cpp @@ -81,6 +81,11 @@ acttable_t CWeaponStunStick::m_acttable[] = #endif { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, true }, +#ifdef EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_IDLE, ACT_IDLE_MELEE, false }, + { ACT_RUN, ACT_RUN_MELEE, false }, + { ACT_WALK, ACT_WALK_MELEE, false }, +#endif }; IMPLEMENT_ACTTABLE(CWeaponStunStick); diff --git a/sp/src/game/shared/hl2mp/weapon_stunstick.h b/sp/src/game/shared/hl2mp/weapon_stunstick.h index da25999c..93688915 100644 --- a/sp/src/game/shared/hl2mp/weapon_stunstick.h +++ b/sp/src/game/shared/hl2mp/weapon_stunstick.h @@ -83,6 +83,12 @@ public: float GetDamageForActivity( Activity hitActivity ); +#ifdef MAPBASE + // Don't use backup activities + acttable_t *GetBackupActivityList() { return NULL; } + int GetBackupActivityListCount() { return 0; } +#endif + CWeaponStunStick( const CWeaponStunStick & ); private: diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp index 7bd4862c..d9155f32 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.cpp @@ -398,9 +398,10 @@ bool CWeaponCustomScripted::Reload( void ) return BaseClass::Reload(); } -void CWeaponCustomScripted::Reload_NPC( void ) +void CWeaponCustomScripted::Reload_NPC( bool bPlaySound ) { - SIMPLE_VOID_OVERRIDE( Reload_NPC, NULL ); + ScriptVariant_t pArgs[] = { bPlaySound }; + SIMPLE_VOID_OVERRIDE( Reload_NPC, pArgs ); BaseClass::Reload_NPC(); } diff --git a/sp/src/game/shared/mapbase/weapon_custom_scripted.h b/sp/src/game/shared/mapbase/weapon_custom_scripted.h index f1493ce0..924f1031 100644 --- a/sp/src/game/shared/mapbase/weapon_custom_scripted.h +++ b/sp/src/game/shared/mapbase/weapon_custom_scripted.h @@ -77,7 +77,7 @@ public: void FinishReload( void ); void AbortReload( void ); bool Reload( void ); - void Reload_NPC( void ); + void Reload_NPC( bool bPlaySound = true ); // Weapon firing void PrimaryAttack( void ); // do "+ATTACK"