diff --git a/src/game/client/c_basecombatcharacter.cpp b/src/game/client/c_basecombatcharacter.cpp index 6b6ac08317d..1be649d41f4 100644 --- a/src/game/client/c_basecombatcharacter.cpp +++ b/src/game/client/c_basecombatcharacter.cpp @@ -109,6 +109,25 @@ void C_BaseCombatCharacter::DoMuzzleFlash() } } +#ifdef MAPBASE +// UNDONE: Should these operate on a list of weapon/items +Activity C_BaseCombatCharacter::Weapon_TranslateActivity( Activity baseAct, bool *pRequired ) +{ + Activity translated = baseAct; + + if ( m_hActiveWeapon ) + { + translated = m_hActiveWeapon->ActivityOverride( baseAct, pRequired ); + } + else if (pRequired) + { + *pRequired = false; + } + + return translated; +} +#endif + #ifdef GLOWS_ENABLE //----------------------------------------------------------------------------- // Purpose: diff --git a/src/game/client/c_basecombatcharacter.h b/src/game/client/c_basecombatcharacter.h index 053dd7dc1ab..92f834e9249 100644 --- a/src/game/client/c_basecombatcharacter.h +++ b/src/game/client/c_basecombatcharacter.h @@ -116,6 +116,11 @@ class C_BaseCombatCharacter : public C_BaseFlex HSCRIPT ScriptGetWeapon( int i ); #endif +#ifdef MAPBASE + virtual Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired ); + virtual Activity Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired = false, C_BaseCombatWeapon *pSpecificWeapon = NULL ); +#endif + public: float m_flNextAttack; diff --git a/src/game/client/c_baseplayer.h b/src/game/client/c_baseplayer.h index b6ffbfe1c27..f00a1039418 100644 --- a/src/game/client/c_baseplayer.h +++ b/src/game/client/c_baseplayer.h @@ -131,6 +131,10 @@ class C_BasePlayer : public C_BaseCombatCharacter, public CGameEventListener virtual Vector Weapon_ShootPosition(); virtual void Weapon_DropPrimary( void ) {} +#ifdef MAPBASE + virtual Activity Weapon_TranslateActivity( Activity baseAct, bool *pRequired = NULL ); +#endif + virtual Vector GetAutoaimVector( float flScale ); void SetSuitUpdate(const char *name, int fgroup, int iNoRepeat); diff --git a/src/game/server/basecombatcharacter.cpp b/src/game/server/basecombatcharacter.cpp index a45b72ed8f4..5d09d782024 100644 --- a/src/game/server/basecombatcharacter.cpp +++ b/src/game/server/basecombatcharacter.cpp @@ -2800,76 +2800,6 @@ bool CBaseCombatCharacter::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) return true; } -#ifdef MAPBASE - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -static Activity Weapon_BackupActivityFromList( CBaseCombatCharacter *pBCC, acttable_t *pTable, int actCount, Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pWeapon ) -{ - int i = 0; - for ( ; i < actCount; i++, pTable++ ) - { - if ( activity == pTable->baseAct ) - { - // Don't pick backup activities we don't actually have an animation for. - if (!pBCC->GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct)) - break; - - return (Activity)pTable->weaponAct; - } - } - - // We didn't succeed in finding an activity. See if we can recurse - acttable_t *pBackupTable = CBaseCombatWeapon::GetDefaultBackupActivityList( pTable - i, actCount ); - if (pBackupTable) - { - return Weapon_BackupActivityFromList( pBCC, pBackupTable, actCount, activity, weaponTranslationWasRequired, pWeapon ); - } - - return activity; -} - -//----------------------------------------------------------------------------- -// Purpose: Uses an activity from a different weapon when the activity we were originally looking for does not exist on this character. -// This gives NPCs and players the ability to use weapons they are otherwise unable to use. -//----------------------------------------------------------------------------- -Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pSpecificWeapon ) -{ - CBaseCombatWeapon *pWeapon = pSpecificWeapon ? pSpecificWeapon : GetActiveWeapon(); - if (!pWeapon) - return activity; - - // Make sure the weapon allows this activity to have a backup. - if (!pWeapon->SupportsBackupActivity(activity)) - return activity; - - // UNDONE: Sometimes, a NPC is supposed to use the default activity. Return that if the weapon translation was "not required" and we have an original activity. - /* - if (!weaponTranslationWasRequired && GetModelPtr()->HaveSequenceForActivity(activity) && !IsPlayer()) - { - return activity; - } - */ - - acttable_t *pTable = pWeapon->GetBackupActivityList(); - int actCount = pWeapon->GetBackupActivityListCount(); - if (!pTable) - { - // Look for a default list - acttable_t *pTable = pWeapon->ActivityList( actCount ); - pTable = CBaseCombatWeapon::GetDefaultBackupActivityList( pTable, actCount ); - } - - if (pTable && GetModelPtr()) - { - return Weapon_BackupActivityFromList( this, pTable, actCount, activity, weaponTranslationWasRequired, pWeapon ); - } - - return activity; -} -#endif - //----------------------------------------------------------------------------- // Purpose: // Input : diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index cbedca4487c..fb2da48eef9 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -8149,33 +8149,6 @@ void CBasePlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon ) } } -#ifdef MAPBASE -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -Activity CBasePlayer::Weapon_TranslateActivity( Activity baseAct, bool *pRequired ) -{ - Activity weaponTranslation = BaseClass::Weapon_TranslateActivity( baseAct, pRequired ); - - if ( GetActiveWeapon() && !GetActiveWeapon()->IsWeaponVisible() && baseAct != ACT_ARM ) - { - // Our weapon is holstered. Use the base activity. - return baseAct; - } - if ( GetModelPtr() && (!GetModelPtr()->HaveSequenceForActivity(weaponTranslation) || baseAct == weaponTranslation) ) - { - // This is used so players can fall back to backup activities in the same way NPCs in Mapbase can - Activity backupActivity = Weapon_BackupActivity(baseAct, pRequired ? *pRequired : false); - if ( baseAct != backupActivity && GetModelPtr()->HaveSequenceForActivity(backupActivity) ) - return backupActivity; - - return baseAct; - } - - return weaponTranslation; -} -#endif - //========================================================= // HasNamedPlayerItem Does the player already have this item? diff --git a/src/game/server/soundent.cpp b/src/game/server/soundent.cpp index a84f767645f..d28125f1a64 100644 --- a/src/game/server/soundent.cpp +++ b/src/game/server/soundent.cpp @@ -9,6 +9,9 @@ #include "soundent.h" #include "game.h" #include "world.h" +#ifdef MAPBASE_MP +#include "ai_basenpc.h" +#endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -459,6 +462,35 @@ void CSoundEnt::InsertSound ( int iType, const Vector &vecOrigin, int iVolume, f if ( !g_pSoundEnt ) return; +#if defined(MAPBASE_MP) && defined(HL2MP) + // Mapbase adds AI sounds to HL2DM weapons, but this fills up the list very quickly and isn't needed when NPCs aren't actually being used + // Ignore weapon sounds when we're reasonably certain they wouldn't be useful + if ( soundChannelIndex == SOUNDENT_CHANNEL_WEAPON ) + { + bool bHasNPCNearby = false; + CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); + for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) + { + CAI_BaseNPC *pNPC = ppAIs[i]; + + if ( !pNPC->IsAlive() || pNPC == pOwner ) + continue; + + // Is there any chance this NPC would hear me? + Vector vecDistSqr = vecOrigin - pNPC->EarPosition(); + if (vecDistSqr.LengthSqr() > Square(iVolume)) + continue; + + bHasNPCNearby = true; + break; + } + + // Just don't if there's no NPC nearby + if (!bHasNPCNearby) + return; + } +#endif + if( soundChannelIndex == SOUNDENT_CHANNEL_UNSPECIFIED ) { // No sound channel specified. So just make a new sound. diff --git a/src/game/shared/ai_basenpc_shared.h b/src/game/shared/ai_basenpc_shared.h new file mode 100644 index 00000000000..615a472140b --- /dev/null +++ b/src/game/shared/ai_basenpc_shared.h @@ -0,0 +1,21 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef AI_BASENPC_SHARED_H +#define AI_BASENPC_SHARED_H + +#ifdef CLIENT_DLL +#include "c_ai_basenpc.h" +#else +#include "ai_basenpc.h" +#endif + +#ifdef CLIENT_DLL +#define CAI_BaseNPC C_AI_BaseNPC +#endif + +#endif // AI_BASENPC_SHARED_H diff --git a/src/game/shared/basecombatcharacter_shared.cpp b/src/game/shared/basecombatcharacter_shared.cpp index d937e59a600..3fdd8bafe86 100644 --- a/src/game/shared/basecombatcharacter_shared.cpp +++ b/src/game/shared/basecombatcharacter_shared.cpp @@ -237,6 +237,75 @@ void CBaseCombatCharacter::InputSetBloodColor( inputdata_t &inputdata ) } #endif +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +static Activity Weapon_BackupActivityFromList( CBaseCombatCharacter *pBCC, acttable_t *pTable, int actCount, Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pWeapon ) +{ + int i = 0; + for ( ; i < actCount; i++, pTable++ ) + { + if ( activity == pTable->baseAct ) + { + // Don't pick backup activities we don't actually have an animation for. + if (!pBCC->GetModelPtr()->HaveSequenceForActivity(pTable->weaponAct)) + break; + + return (Activity)pTable->weaponAct; + } + } + + // We didn't succeed in finding an activity. See if we can recurse + acttable_t *pBackupTable = CBaseCombatWeapon::GetDefaultBackupActivityList( pTable - i, actCount ); + if (pBackupTable) + { + return Weapon_BackupActivityFromList( pBCC, pBackupTable, actCount, activity, weaponTranslationWasRequired, pWeapon ); + } + + return activity; +} + +//----------------------------------------------------------------------------- +// Purpose: Uses an activity from a different weapon when the activity we were originally looking for does not exist on this character. +// This gives NPCs and players the ability to use weapons they are otherwise unable to use. +//----------------------------------------------------------------------------- +Activity CBaseCombatCharacter::Weapon_BackupActivity( Activity activity, bool weaponTranslationWasRequired, CBaseCombatWeapon *pSpecificWeapon ) +{ + CBaseCombatWeapon *pWeapon = pSpecificWeapon ? pSpecificWeapon : GetActiveWeapon(); + if (!pWeapon) + return activity; + + // Make sure the weapon allows this activity to have a backup. + if (!pWeapon->SupportsBackupActivity(activity)) + return activity; + + // UNDONE: Sometimes, a NPC is supposed to use the default activity. Return that if the weapon translation was "not required" and we have an original activity. + /* + if (!weaponTranslationWasRequired && GetModelPtr()->HaveSequenceForActivity(activity) && !IsPlayer()) + { + return activity; + } + */ + + acttable_t *pTable = pWeapon->GetBackupActivityList(); + int actCount = pWeapon->GetBackupActivityListCount(); + if (!pTable) + { + // Look for a default list + acttable_t *pDefaultTable = pWeapon->ActivityList( actCount ); + pTable = CBaseCombatWeapon::GetDefaultBackupActivityList( pDefaultTable, actCount ); + } + + if (pTable && GetModelPtr()) + { + return Weapon_BackupActivityFromList( this, pTable, actCount, activity, weaponTranslationWasRequired, pWeapon ); + } + + return activity; +} +#endif + //----------------------------------------------------------------------------- /** The main visibility check. Checks all the entity specific reasons that could diff --git a/src/game/shared/basecombatweapon_shared.cpp b/src/game/shared/basecombatweapon_shared.cpp index e1965ebba2b..0a671ab51ba 100644 --- a/src/game/shared/basecombatweapon_shared.cpp +++ b/src/game/shared/basecombatweapon_shared.cpp @@ -1158,7 +1158,7 @@ WeaponClass_t CBaseCombatWeapon::WeaponClassFromString(const char *str) return WEPCLASS_INVALID; } -#ifdef HL2_DLL +#if defined(HL2_DLL) || defined(HL2MP) // HL2MP is effectively used here as "present on client in MP" extern acttable_t *GetSMG1Acttable(); extern int GetSMG1ActtableCount(); @@ -1206,7 +1206,7 @@ int CBaseCombatWeapon::GetBackupActivityListCount() //----------------------------------------------------------------------------- acttable_t *CBaseCombatWeapon::GetDefaultBackupActivityList( acttable_t *pTable, int &actCount ) { -#ifdef HL2_DLL +#if defined(HL2_DLL) || defined(HL2MP) // HL2MP is effectively used here as "present on client in MP" // Ensure this isn't already a default backup activity list if (pTable == GetSMG1Acttable() || pTable == GetPistolActtable()) return NULL; @@ -1318,6 +1318,9 @@ void CBaseCombatWeapon::SetActivity( Activity act, float duration ) { //Adrian: Oh man... #if !defined( CLIENT_DLL ) && (defined( HL2MP ) || defined( PORTAL )) +#ifdef MAPBASE_MP + if ( GetOwner() && GetOwner()->IsPlayer() ) +#endif SetModel( GetWorldModel() ); #endif @@ -1329,6 +1332,9 @@ void CBaseCombatWeapon::SetActivity( Activity act, float duration ) //Adrian: Oh man again... #if !defined( CLIENT_DLL ) && (defined( HL2MP ) || defined( PORTAL )) +#ifdef MAPBASE_MP + if ( GetOwner() && GetOwner()->IsPlayer() ) +#endif SetModel( GetViewModel() ); #endif diff --git a/src/game/shared/baseplayer_shared.cpp b/src/game/shared/baseplayer_shared.cpp index ba0294d4ac9..b703a2e0f11 100644 --- a/src/game/shared/baseplayer_shared.cpp +++ b/src/game/shared/baseplayer_shared.cpp @@ -875,6 +875,41 @@ void CBasePlayer::SelectLastItem(void) } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +Activity CBasePlayer::Weapon_TranslateActivity( Activity baseAct, bool *pRequired ) +{ + Activity weaponTranslation = BaseClass::Weapon_TranslateActivity( baseAct, pRequired ); + +#ifdef CLIENT_DLL + // Since other players' weapons have invisible viewmodels + bool bWeaponNotVisible = (GetActiveWeapon() && GetActiveWeapon()->IsEffectActive( EF_NODRAW )) ? true : false; +#else + bool bWeaponNotVisible = (GetActiveWeapon() && !GetActiveWeapon()->IsWeaponVisible()) ? true : false; +#endif + + if ( bWeaponNotVisible && baseAct != ACT_ARM ) + { + // Our weapon is holstered. Use the base activity. + return baseAct; + } + if ( GetModelPtr() && (!GetModelPtr()->HaveSequenceForActivity(weaponTranslation) || baseAct == weaponTranslation) ) + { + // This is used so players can fall back to backup activities in the same way NPCs in Mapbase can + Activity backupActivity = Weapon_BackupActivity(baseAct, pRequired ? *pRequired : false); + if ( baseAct != backupActivity && GetModelPtr()->HaveSequenceForActivity(backupActivity) ) + return backupActivity; + + return baseAct; + } + + return weaponTranslation; +} +#endif + + //----------------------------------------------------------------------------- // Purpose: Abort any reloads we're in //----------------------------------------------------------------------------- diff --git a/src/game/shared/hl2mp/weapon_357.cpp b/src/game/shared/hl2mp/weapon_357.cpp index cac81b01bd3..b2d052a57b0 100644 --- a/src/game/shared/hl2mp/weapon_357.cpp +++ b/src/game/shared/hl2mp/weapon_357.cpp @@ -8,12 +8,14 @@ #include "cbase.h" #include "npcevent.h" #include "in_buttons.h" +#include "ai_basenpc_shared.h" #ifdef CLIENT_DLL #include "c_hl2mp_player.h" #include #else #include "hl2mp_player.h" + #include "te_effect_dispatch.h" #endif #include "weapon_hl2mpbasehlmpcombatweapon.h" @@ -26,6 +28,11 @@ // CWeapon357 //----------------------------------------------------------------------------- +#ifdef MAPBASE +extern acttable_t *GetPistolActtable(); +extern int GetPistolActtableCount(); +#endif + class CWeapon357 : public CBaseHL2MPCombatWeapon { DECLARE_CLASS( CWeapon357, CBaseHL2MPCombatWeapon ); @@ -34,11 +41,54 @@ class CWeapon357 : public CBaseHL2MPCombatWeapon CWeapon357( void ); void PrimaryAttack( void ); + +#ifdef MAPBASE + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + +#ifndef CLIENT_DLL + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } +#endif + + virtual int GetMinBurst() { return 1; } + virtual int GetMaxBurst() { return 1; } + virtual float GetMinRestTime( void ) { return 1.0f; } + virtual float GetMaxRestTime( void ) { return 2.5f; } + + virtual float GetFireRate( void ) { return 1.0f; } + + virtual const Vector& GetBulletSpread( void ) + { + static Vector cone = VECTOR_CONE_15DEGREES; + if (!GetOwner() || !GetOwner()->IsNPC()) + return cone; + + static Vector NPCCone = VECTOR_CONE_5DEGREES; + +#ifndef CLIENT_DLL + static Vector AllyCone = VECTOR_CONE_2DEGREES; + if( GetOwner()->MyNPCPointer()->IsPlayerAlly() ) + { + // 357 allies should be cooler + return AllyCone; + } +#endif + + return NPCCone; + } + + 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_NETWORKCLASS(); DECLARE_PREDICTABLE(); -#ifndef CLIENT_DLL DECLARE_ACTTABLE(); +#ifndef CLIENT_DLL + DECLARE_DATADESC(); #endif private: @@ -59,23 +109,140 @@ PRECACHE_WEAPON_REGISTER( weapon_357 ); #ifndef CLIENT_DLL -acttable_t CWeapon357::m_acttable[] = +BEGIN_DATADESC( CWeapon357 ) +END_DATADESC() +#endif + +#ifdef MAPBASE +acttable_t CWeapon357::m_acttable[] = { - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false }, - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, false }, -}; +#if 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 }, + { ACT_RELOAD, ACT_RELOAD_PISTOL, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_PISTOL, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_PISTOL, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_PISTOL,true }, + { ACT_RELOAD_LOW, ACT_RELOAD_PISTOL_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_PISTOL_LOW, false }, + { ACT_COVER_LOW, ACT_COVER_PISTOL_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_PISTOL_LOW, false }, + { 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) +#if 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 }, -IMPLEMENT_ACTTABLE( CWeapon357 ); +#if 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 }, + +#if 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 }, + + // Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_PISTOL, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_PISTOL, 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, 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, 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 + + // Crouch activities + { ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false }, + { ACT_CROUCHIDLE_AIM_STIMULATED,ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + { ACT_CROUCHIDLE_AGITATED, ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + + // Readiness translations + { ACT_READINESS_RELAXED_TO_STIMULATED, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED, false }, + { 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 }, + +#if 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 #ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_REVOLVER, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_REVOLVER, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_REVOLVER, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_REVOLVER, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_REVOLVER, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_REVOLVER, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_REVOLVER, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_REVOLVER, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_REVOLVER, false }, +#else + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false }, +#endif +#endif +}; + + +IMPLEMENT_ACTTABLE( CWeapon357 ); + // Allows Weapon_BackupActivity() to access the 357's activity table. acttable_t *Get357Acttable() { @@ -88,8 +255,6 @@ int Get357ActtableCount() } #endif -#endif - //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- @@ -97,8 +262,104 @@ CWeapon357::CWeapon357( void ) { m_bReloadsSingly = false; m_bFiresUnderwater = false; + +#ifdef MAPBASE + m_fMinRange1 = 24; + m_fMaxRange1 = 1000; + m_fMinRange2 = 24; + m_fMaxRange2 = 200; +#endif } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeapon357::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + + switch( pEvent->event ) + { +#ifndef CLIENT_DLL + case EVENT_WEAPON_RELOAD: + { + CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); + CEffectData data; + + // Emit six spent shells + for ( int i = 0; i < 6; i++ ) + { + data.m_vOrigin = pOwner->WorldSpaceCenter() + RandomVector( -4, 4 ); + data.m_vAngles = QAngle( 90, random->RandomInt( 0, 360 ), 0 ); + data.m_nEntIndex = entindex(); + + DispatchEffect( "ShellEject", data ); + } + + break; + } +#endif +#ifdef MAPBASE + case EVENT_WEAPON_PISTOL_FIRE: + { +#ifdef CLIENT_DLL + WeaponSound( SINGLE_NPC ); +#else + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + Assert( npc != NULL ); + + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +#endif + } + break; + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; +#endif + } +} + +#ifndef CLIENT_DLL +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeapon357::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + WeaponSound( SINGLE_NPC ); + + FireBulletsInfo_t info( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType ); + info.m_iTracerFreq = 1; + + pOperator->FireBullets( info ); + + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Some things need this. (e.g. the new Force(X)Fire inputs or blindfire actbusy) +//----------------------------------------------------------------------------- +void CWeapon357::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +} +#endif +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -139,7 +400,7 @@ void CWeapon357::PrimaryAttack( void ) m_iClip1--; Vector vecSrc = pPlayer->Weapon_ShootPosition(); - Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); + Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES ); FireBulletsInfo_t info( 1, vecSrc, vecAiming, vec3_origin, MAX_TRACE_LENGTH, m_iPrimaryAmmoType ); info.m_pAttacker = pPlayer; @@ -147,6 +408,10 @@ void CWeapon357::PrimaryAttack( void ) // Fire the bullets, and force the first shot to be perfectly accuracy pPlayer->FireBullets( info ); +#ifndef CLIENT_DLL + pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 ); +#endif + #ifdef CLIENT_DLL //Disorient the player if ( prediction->IsFirstTimePredicted() ) @@ -162,6 +427,10 @@ void CWeapon357::PrimaryAttack( void ) pPlayer->ViewPunch( QAngle( -8, random->RandomFloat( -2, 2 ), 0 ) ); +#ifndef CLIENT_DLL + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 600, 0.2, GetOwner() ); +#endif + if ( !m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) { // HEV suit - indicate out of ammo condition diff --git a/src/game/shared/hl2mp/weapon_ar2.cpp b/src/game/shared/hl2mp/weapon_ar2.cpp index 702aa1a81e9..296fb893729 100644 --- a/src/game/shared/hl2mp/weapon_ar2.cpp +++ b/src/game/shared/hl2mp/weapon_ar2.cpp @@ -7,14 +7,19 @@ #include "cbase.h" #include "npcevent.h" +#include "ai_basenpc_shared.h" #ifdef CLIENT_DLL #include "c_hl2mp_player.h" #include "c_te_effect_dispatch.h" #else #include "hl2mp_player.h" + #include "soundent.h" #include "te_effect_dispatch.h" #include "prop_combine_ball.h" +#ifdef MAPBASE + #include "npc_playercompanion.h" +#endif #endif #include "weapon_ar2.h" @@ -33,6 +38,16 @@ ConVar sk_weapon_ar2_alt_fire_mass( "sk_weapon_ar2_alt_fire_mass", "150" ); //========================================================= //========================================================= +#ifndef CLIENT_DLL +BEGIN_DATADESC( CWeaponAR2 ) + + DEFINE_FIELD( m_flDelayedFire, FIELD_TIME ), + DEFINE_FIELD( m_bShotDelayed, FIELD_BOOLEAN ), + //DEFINE_FIELD( m_nVentPose, FIELD_INTEGER ), + +END_DATADESC() +#endif + IMPLEMENT_NETWORKCLASS_ALIASED( WeaponAR2, DT_WeaponAR2 ) @@ -58,18 +73,137 @@ LINK_ENTITY_TO_CLASS( weapon_ar2, CWeaponAR2 ); PRECACHE_WEAPON_REGISTER(weapon_ar2); -#ifndef CLIENT_DLL - acttable_t CWeaponAR2::m_acttable[] = { - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR2, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_AR2, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_AR2, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_AR2, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR2, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_AR2, false }, - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR2, false }, +#if AR2_ACTIVITY_FIX == 1 + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR2, true }, + { ACT_RELOAD, ACT_RELOAD_AR2, true }, + { ACT_IDLE, ACT_IDLE_AR2, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_AR2, false }, + + { ACT_WALK, ACT_WALK_AR2, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_AR2_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_AR2_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_AR2, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_AR2_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_AR2_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_AR2, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_AR2_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_AR2_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_AR2_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_AR2_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_AR2, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_AR2_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_AR2_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_AR2, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_AR2_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_AR2_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims +//End readiness activities + + { ACT_WALK_AIM, ACT_WALK_AIM_AR2, true }, + { 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_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_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_AR2_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_AR2, true }, +// { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, +#else + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR2, true }, + { ACT_RELOAD, ACT_RELOAD_SMG1, true }, // FIXME: hook to AR2 unique + { ACT_IDLE, ACT_IDLE_SMG1, true }, // FIXME: hook to AR2 unique + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG1, true }, // FIXME: hook to AR2 unique + + { ACT_WALK, ACT_WALK_RIFLE, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SMG1_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims +//End readiness activities + + { 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 }, + { ACT_RUN, ACT_RUN_RIFLE, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, 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 }, // FIXME: hook to AR2 unique + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_AR2_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SMG1_LOW, true }, // FIXME: hook to AR2 unique + { ACT_RELOAD_LOW, ACT_RELOAD_SMG1_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, +// { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, +#endif + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#endif + +#if 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 + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR2, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_AR2, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_AR2, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_AR2, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_AR2, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_AR2, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_AR2, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_AR2, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponAR2); @@ -87,8 +221,6 @@ int GetAR2ActtableCount() } #endif -#endif - CWeaponAR2::CWeaponAR2( ) { m_fMinRange1 = 65; @@ -202,6 +334,9 @@ void CWeaponAR2::DelayedAttack( void ) // Register a muzzleflash for the AI pOwner->DoMuzzleFlash(); +#ifndef CLIENT_DLL + pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 ); +#endif WeaponSound( WPN_DOUBLE ); @@ -234,8 +369,6 @@ void CWeaponAR2::DelayedAttack( void ) angles.y += random->RandomInt( -4, 4 ); angles.z = 0; -// pOwner->SnapEyeAngles( angles ); - pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -12, -8 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) ); // Decrease ammo @@ -273,6 +406,10 @@ void CWeaponAR2::SecondaryAttack( void ) m_bShotDelayed = true; m_flNextEmptySoundTime = m_flNextPrimaryAttack = m_flNextSecondaryAttack = m_flDelayedFire = gpGlobals->curtime + 0.5f; +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#endif + SendWeaponAnim( ACT_VM_FIDGET ); WeaponSound( SPECIAL1 ); } @@ -309,6 +446,184 @@ bool CWeaponAR2::Reload( void ) return BaseClass::Reload(); } +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOperator - +//----------------------------------------------------------------------------- +void CWeaponAR2::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles ) +{ + Vector vecShootOrigin, vecShootDir; + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + Assert( npc != NULL ); + + if ( bUseWeaponAngles ) + { + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + } + else + { + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + } + + WeaponSoundRealtime( SINGLE_NPC ); + + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + FireBulletsInfo_t info( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType ); + info.m_iTracerFreq = 2; + + pOperator->FireBullets( info ); + + // NOTENOTE: This is overriden on the client-side + // pOperator->DoMuzzleFlash(); + + m_iClip1 = m_iClip1 - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponAR2::FireNPCSecondaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles ) +{ + WeaponSound( WPN_DOUBLE ); + + if ( !GetOwner() ) + return; + + CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); + if ( !pNPC ) + return; + + // Fire! + Vector vecSrc; + Vector vecAiming; + + if ( bUseWeaponAngles ) + { + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecSrc, angShootDir ); + AngleVectors( angShootDir, &vecAiming ); + } + else + { + vecSrc = pNPC->Weapon_ShootPosition( ); + + Vector vecTarget; + +#ifdef MAPBASE + // It's shared across all NPCs now that it's available on more than just soldiers on more than just the AR2. + vecTarget = pNPC->GetAltFireTarget(); +#else + CNPC_Combine *pSoldier = dynamic_cast( pNPC ); + if ( pSoldier ) + { + // In the distant misty past, elite soldiers tried to use bank shots. + // Therefore, we must ask them specifically what direction they are shooting. + vecTarget = pSoldier->GetAltFireTarget(); + } +#ifdef MAPBASE + else if ( CNPC_PlayerCompanion *pCompanion = dynamic_cast( pNPC ) ) + { + // Companions can use energy balls now. Isn't that lovely? + vecTarget = pCompanion->GetAltFireTarget(); + } +#endif + else + { + // All other users of the AR2 alt-fire shoot directly at their enemy. + if ( !pNPC->GetEnemy() ) + return; + + vecTarget = pNPC->GetEnemy()->BodyTarget( vecSrc ); + } +#endif + + vecAiming = vecTarget - vecSrc; + VectorNormalize( vecAiming ); + } + + Vector impactPoint = vecSrc + ( vecAiming * MAX_TRACE_LENGTH ); + + float flAmmoRatio = 1.0f; + float flDuration = RemapValClamped( flAmmoRatio, 0.0f, 1.0f, 0.5f, sk_weapon_ar2_alt_fire_duration.GetFloat() ); + float flRadius = RemapValClamped( flAmmoRatio, 0.0f, 1.0f, 4.0f, sk_weapon_ar2_alt_fire_radius.GetFloat() ); + + // Fire the bullets + Vector vecVelocity = vecAiming * 1000.0f; + + // Fire the combine ball +#ifdef MAPBASE + CBaseEntity *pBall = CreateCombineBall( vecSrc, + vecVelocity, + flRadius, + sk_weapon_ar2_alt_fire_mass.GetFloat(), + flDuration, + pNPC ); + + variant_t var; + var.SetEntity(pBall); + pNPC->FireNamedOutput("OnThrowGrenade", var, pBall, pNPC); +#else + CreateCombineBall( vecSrc, + vecVelocity, + flRadius, + sk_weapon_ar2_alt_fire_mass.GetFloat(), + flDuration, + pNPC ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponAR2::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + if ( bSecondary ) + { + FireNPCSecondaryAttack( pOperator, true ); + } + else + { + // Ensure we have enough rounds in the clip + m_iClip1++; + + FireNPCPrimaryAttack( pOperator, true ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEvent - +// *pOperator - +//----------------------------------------------------------------------------- +void CWeaponAR2::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_AR2: + { + FireNPCPrimaryAttack( pOperator, false ); + } + break; + + case EVENT_WEAPON_AR2_ALTFIRE: + { + FireNPCSecondaryAttack( pOperator, false ); + } + break; + + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/src/game/shared/hl2mp/weapon_ar2.h b/src/game/shared/hl2mp/weapon_ar2.h index fa06cef5468..53ebc2aef8b 100644 --- a/src/game/shared/hl2mp/weapon_ar2.h +++ b/src/game/shared/hl2mp/weapon_ar2.h @@ -32,8 +32,9 @@ class CWeaponAR2 : public CHL2MPMachineGun DECLARE_PREDICTABLE(); #ifndef CLIENT_DLL - DECLARE_ACTTABLE(); + DECLARE_DATADESC(); #endif + DECLARE_ACTTABLE(); void ItemPostFrame( void ); void Precache( void ); @@ -45,12 +46,23 @@ class CWeaponAR2 : public CHL2MPMachineGun void AddViewKick( void ); +#ifndef CLIENT_DLL + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles ); + void FireNPCSecondaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#endif + int GetMinBurst( void ) { return 2; } int GetMaxBurst( void ) { return 5; } float GetFireRate( void ) { return 0.1f; } bool CanHolster( void ); bool Reload( void ); + +#ifndef CLIENT_DLL + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } +#endif Activity GetPrimaryAttackActivity( void ); diff --git a/src/game/shared/hl2mp/weapon_crossbow.cpp b/src/game/shared/hl2mp/weapon_crossbow.cpp index 6b3d280ee44..4047a9edffb 100644 --- a/src/game/shared/hl2mp/weapon_crossbow.cpp +++ b/src/game/shared/hl2mp/weapon_crossbow.cpp @@ -7,6 +7,7 @@ #include "cbase.h" #include "npcevent.h" #include "in_buttons.h" +#include "ai_basenpc_shared.h" #ifdef CLIENT_DLL #include "c_hl2mp_player.h" @@ -40,8 +41,15 @@ extern ConVar sk_plr_dmg_crossbow; extern ConVar sk_npc_dmg_crossbow; +#ifdef MAPBASE +ConVar weapon_crossbow_new_hit_locations( "weapon_crossbow_new_hit_locations", "1", FCVAR_NONE, "Toggles new crossbow knockback that properly pushes back the correct limbs." ); +#endif + void TE_StickyBolt( IRecipientFilter& filter, float delay, Vector vecDirection, const Vector *origin ); +#define BOLT_SKIN_NORMAL 0 +#define BOLT_SKIN_GLOW 1 + //----------------------------------------------------------------------------- // Crossbow Bolt //----------------------------------------------------------------------------- @@ -50,7 +58,7 @@ class CCrossbowBolt : public CBaseCombatCharacter DECLARE_CLASS( CCrossbowBolt, CBaseCombatCharacter ); public: - CCrossbowBolt() { }; + CCrossbowBolt(); ~CCrossbowBolt(); Class_T Classify( void ) { return CLASS_NONE; } @@ -62,7 +70,20 @@ class CCrossbowBolt : public CBaseCombatCharacter void BoltTouch( CBaseEntity *pOther ); bool CreateVPhysics( void ); unsigned int PhysicsSolidMaskForEntity() const; +#ifdef MAPBASE + static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, CBaseCombatCharacter *pentOwner = NULL ) + { + return BoltCreate( vecOrigin, angAngles, 0, pentOwner ); + } + static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, int iDamage, CBaseCombatCharacter *pentOwner = NULL ); + + void InputSetDamage( inputdata_t &inputdata ); + float m_flDamage; + + virtual void SetDamage(float flDamage) { m_flDamage = flDamage; } +#else static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, int iDamage, CBasePlayer *pentOwner = NULL ); +#endif protected: @@ -70,8 +91,10 @@ class CCrossbowBolt : public CBaseCombatCharacter CHandle m_pGlowSprite; //CHandle m_pGlowTrail; - + +#ifndef MAPBASE int m_iDamage; +#endif DECLARE_DATADESC(); DECLARE_SERVERCLASS(); @@ -87,12 +110,23 @@ BEGIN_DATADESC( CCrossbowBolt ) DEFINE_FIELD( m_pGlowSprite, FIELD_EHANDLE ), //DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ), +#ifdef MAPBASE + DEFINE_KEYFIELD( m_flDamage, FIELD_FLOAT, "Damage" ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_FLOAT, "SetDamage", InputSetDamage ), +#endif + END_DATADESC() IMPLEMENT_SERVERCLASS_ST( CCrossbowBolt, DT_CrossbowBolt ) END_SEND_TABLE() +#ifdef MAPBASE +CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, int iDamage, CBaseCombatCharacter *pentOwner ) +#else CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, int iDamage, CBasePlayer *pentOwner ) +#endif { // Create a new entity with CCrossbowBolt private data CCrossbowBolt *pBolt = (CCrossbowBolt *)CreateEntityByName( "crossbow_bolt" ); @@ -100,12 +134,27 @@ CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle pBolt->SetAbsAngles( angAngles ); pBolt->Spawn(); pBolt->SetOwnerEntity( pentOwner ); - - pBolt->m_iDamage = iDamage; +#ifdef MAPBASE + if (pentOwner && pentOwner->IsNPC()) + pBolt->m_flDamage = sk_npc_dmg_crossbow.GetFloat(); + else if (iDamage != 0) + pBolt->m_flDamage = (float)iDamage; +#endif return pBolt; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CCrossbowBolt::CCrossbowBolt( void ) +{ +#ifdef MAPBASE + // Independent bolts without m_flDamage set need damage + m_flDamage = sk_plr_dmg_crossbow.GetFloat(); +#endif +} + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -194,14 +243,40 @@ void CCrossbowBolt::Precache( void ) PrecacheModel( "sprites/light_glow02_noz.vmt" ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCrossbowBolt::InputSetDamage( inputdata_t &inputdata ) +{ + m_flDamage = inputdata.value.Float(); +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Input : *pOther - //----------------------------------------------------------------------------- void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) { - if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) ) - return; + if ( pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS | FSOLID_TRIGGER) ) + { + // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them. +#ifdef MAPBASE + // But some physics objects that are also triggers (like weapons) shouldn't go through this check. + // + // Note: rpg_missile has the same code, except it properly accounts for weapons in a different way. + // This was discovered after I implemented this and both work fine, but if this ever causes problems, + // use rpg_missile's implementation: + // + // if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON ) + // + if ( pOther->GetMoveType() == MOVETYPE_NONE && (( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY )) ) +#else + if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) ) +#endif + return; + } if ( pOther->m_takedamage != DAMAGE_NO ) { @@ -212,9 +287,57 @@ void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) ClearMultiDamage(); VectorNormalize( vecNormalizedVel ); +#if defined(HL2_EPISODIC) + //!!!HACKHACK - specific hack for ep2_outland_10 to allow crossbow bolts to pass through her bounding box when she's crouched in front of the player + // (the player thinks they have clear line of sight because Alyx is crouching, but her BBOx is still full-height and blocks crossbow bolts. + if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->Classify() == CLASS_PLAYER_ALLY_VITAL && FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10") ) + { + // Change the owner to stop further collisions with Alyx. We do this by making her the owner. + // The player won't get credit for this kill but at least the bolt won't magically disappear! + SetOwnerEntity( pOther ); + return; + } +#endif//HL2_EPISODIC + +#ifdef MAPBASE + if (weapon_crossbow_new_hit_locations.GetInt() > 0) + { + // A very experimental and weird way of getting a crossbow bolt to deal accurate knockback. + CBaseAnimating *pOtherAnimating = pOther->GetBaseAnimating(); + if (pOtherAnimating && pOtherAnimating->GetModelPtr() && pOtherAnimating->GetModelPtr()->numbones() > 1) + { + int iClosestBone = -1; + float flCurDistSqr = Square(128.0f); + matrix3x4_t bonetoworld; + Vector vecBonePos; + for (int i = 0; i < pOtherAnimating->GetModelPtr()->numbones(); i++) + { + pOtherAnimating->GetBoneTransform( i, bonetoworld ); + MatrixPosition( bonetoworld, vecBonePos ); + + float flDist = vecBonePos.DistToSqr(GetLocalOrigin()); + if (flDist < flCurDistSqr) + { + iClosestBone = i; + flCurDistSqr = flDist; + } + } + + if (iClosestBone != -1) + { + tr.physicsbone = pOtherAnimating->GetPhysicsBone(iClosestBone); + } + } + } +#endif + if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->IsNPC() ) { +#ifdef MAPBASE + CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_flDamage, DMG_NEVERGIB ); +#else CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_iDamage, DMG_NEVERGIB ); +#endif dmgInfo.AdjustPlayerDamageInflictedForSkillLevel(); CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f ); dmgInfo.SetDamagePosition( tr.endpos ); @@ -222,7 +345,11 @@ void CCrossbowBolt::BoltTouch( CBaseEntity *pOther ) } else { +#ifdef MAPBASE + CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_flDamage, DMG_BULLET | DMG_NEVERGIB ); +#else CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_iDamage, DMG_BULLET | DMG_NEVERGIB ); +#endif CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f ); dmgInfo.SetDamagePosition( tr.endpos ); pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr ); @@ -372,6 +499,9 @@ void CCrossbowBolt::BubbleThink( void ) SetNextThink( gpGlobals->curtime + 0.1f ); + // Make danger sounds out in front of me, to scare snipers back into their hole + CSoundEnt::InsertSound( SOUND_DANGER_SNIPERONLY, GetAbsOrigin() + GetAbsVelocity() * 0.2, 120.0f, 0.5f, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + if ( GetWaterLevel() == 0 ) return; @@ -388,9 +518,9 @@ void CCrossbowBolt::BubbleThink( void ) #define CWeaponCrossbow C_WeaponCrossbow #endif -class CWeaponCrossbow : public CBaseHL2MPCombatWeapon +class CWeaponCrossbow : public CBaseHLCombatWeapon { - DECLARE_CLASS( CWeaponCrossbow, CBaseHL2MPCombatWeapon ); + DECLARE_CLASS( CWeaponCrossbow, CBaseHLCombatWeapon ); public: CWeaponCrossbow( void ); @@ -404,23 +534,55 @@ class CWeaponCrossbow : public CBaseHL2MPCombatWeapon virtual void ItemPostFrame( void ); virtual void ItemBusyFrame( void ); virtual bool SendWeaponAnim( int iActivity ); + virtual bool IsWeaponZoomed() { return m_bInZoom; } -#ifndef CLIENT_DLL virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + +#ifdef MAPBASE +#ifndef CLIENT_DLL + virtual void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + virtual void Reload_NPC( bool bPlaySound = true ); + + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } +#endif + + virtual int GetMinBurst() { return 1; } + virtual int GetMaxBurst() { return 1; } + + virtual float GetMinRestTime( void ) { return 3.0f; } // 1.5f + virtual float GetMaxRestTime( void ) { return 3.0f; } // 2.0f + + virtual float GetFireRate( void ) { return 5.0f; } + + virtual const Vector& GetBulletSpread( void ) + { + static Vector cone = VECTOR_CONE_15DEGREES; + if (!GetOwner() || !GetOwner()->IsNPC()) + return cone; + + static Vector NPCCone = VECTOR_CONE_5DEGREES; + + return NPCCone; + } #endif DECLARE_NETWORKCLASS(); DECLARE_PREDICTABLE(); #ifndef CLIENT_DLL - DECLARE_ACTTABLE(); + DECLARE_DATADESC(); #endif + DECLARE_ACTTABLE(); private: void SetSkin( int skinNum ); void CheckZoomToggle( void ); void FireBolt( void ); +#ifdef MAPBASE + void SetBolt( int iSetting ); + void FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin, Vector &vecShootDir ); +#endif void ToggleZoom( void ); // Various states for the crossbow's charger @@ -480,15 +642,140 @@ PRECACHE_WEAPON_REGISTER( weapon_crossbow ); #ifndef CLIENT_DLL +BEGIN_DATADESC( CWeaponCrossbow ) + + DEFINE_FIELD( m_bInZoom, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bMustReload, FIELD_BOOLEAN ), + DEFINE_FIELD( m_nChargeState, FIELD_INTEGER ), + DEFINE_FIELD( m_hChargerSprite, FIELD_EHANDLE ), + +END_DATADESC() + +#endif + +#ifdef MAPBASE acttable_t CWeaponCrossbow::m_acttable[] = { - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_CROSSBOW, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_CROSSBOW, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_CROSSBOW, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_CROSSBOW, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_CROSSBOW, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_CROSSBOW, false }, +#if 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 }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_CROSSBOW_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_CROSSBOW_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_CROSSBOW, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_CROSSBOW_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_CROSSBOW_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_CROSSBOW, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_CROSSBOW_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_CROSSBOW_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_CROSSBOW, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_CROSSBOW_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_CROSSBOW_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_CROSSBOW, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_CROSSBOW_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_CROSSBOW_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_CROSSBOW, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_CROSSBOW_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_CROSSBOW_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_CROSSBOW, false },//always aims +//End readiness activities + + { ACT_WALK, ACT_WALK_CROSSBOW, true }, + { 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_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true }, + { ACT_RELOAD, ACT_RELOAD_SMG1, true }, + { ACT_IDLE, ACT_IDLE_SMG1, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG1, true }, + + { ACT_WALK, ACT_WALK_RIFLE, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SMG1_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims +//End readiness activities + + { 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 }, + { ACT_RUN, ACT_RUN_RIFLE, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, 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_SMG1, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SMG1_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false }, + { 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 + +#if 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 + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_CROSSBOW, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_CROSSBOW, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_CROSSBOW, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_CROSSBOW, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_CROSSBOW, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_CROSSBOW, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_CROSSBOW, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_CROSSBOW, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponCrossbow); @@ -516,6 +803,13 @@ CWeaponCrossbow::CWeaponCrossbow( void ) m_bFiresUnderwater = true; m_bInZoom = false; m_bMustReload = false; + +#ifdef MAPBASE + m_fMinRange1 = 24; + m_fMaxRange1 = 5000; + m_fMinRange2 = 24; + m_fMaxRange2 = 5000; +#endif } #define CROSSBOW_GLOW_SPRITE "sprites/light_glow02_noz.vmt" @@ -560,11 +854,16 @@ void CWeaponCrossbow::PrimaryAttack( void ) SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) ); + CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); + if ( pPlayer ) + { +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif #ifdef GAME_DLL - CBasePlayer *player = ToBasePlayer( GetOwner() ); - if ( player ) - player->OnMyWeaponFired( this ); + pPlayer->OnMyWeaponFired( this ); #endif + } } //----------------------------------------------------------------------------- @@ -642,7 +941,11 @@ void CWeaponCrossbow::FireBolt( void ) else { WeaponSound( EMPTY ); +#ifdef MAPBASE_MP + m_flNextPrimaryAttack = gpGlobals->curtime + 0.15f; +#else m_flNextPrimaryAttack = 0.15; +#endif } return; @@ -660,6 +963,23 @@ void CWeaponCrossbow::FireBolt( void ) QAngle angAiming; VectorAngles( vecAiming, angAiming ); +#if defined(HL2_EPISODIC) + // !!!HACK - the other piece of the Alyx crossbow bolt hack for Outland_10 (see ::BoltTouch() for more detail) + if( FStrEq(STRING(gpGlobals->mapname), "ep2_outland_10") ) + { + trace_t tr; + UTIL_TraceLine( vecSrc, vecSrc + vecAiming * 24.0f, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr ); + + if( tr.m_pEnt != NULL && tr.m_pEnt->Classify() == CLASS_PLAYER_ALLY_VITAL ) + { + // If Alyx is right in front of the player, make sure the bolt starts outside of the player's BBOX, or the bolt + // will instantly collide with the player after the owner of the bolt is switched to Alyx in ::BoltTouch(). We + // avoid this altogether by making it impossible for the bolt to collide with the player. + vecSrc += vecAiming * 24.0f; + } + } +#endif + CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate( vecSrc, angAiming, GetHL2MPWpnData().m_iPlayerDamage, pOwner ); if ( pOwner->GetWaterLevel() == 3 ) @@ -675,11 +995,18 @@ void CWeaponCrossbow::FireBolt( void ) m_iClip1--; +#ifdef MAPBASE + SetBolt( 1 ); +#endif + pOwner->ViewPunch( QAngle( -2, 0, 0 ) ); WeaponSound( SINGLE ); WeaponSound( SPECIAL2 ); +#ifndef CLIENT_DLL + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 200, 0.2 ); +#endif SendWeaponAnim( ACT_VM_PRIMARYATTACK ); if ( !m_iClip1 && pOwner->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ) @@ -694,6 +1021,85 @@ void CWeaponCrossbow::FireBolt( void ) SetChargerState( CHARGER_STATE_DISCHARGE ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: Sets whether or not the bolt is visible +//----------------------------------------------------------------------------- +inline void CWeaponCrossbow::SetBolt( int iSetting ) +{ + int iBody = FindBodygroupByName( "bolt" ); + if (iBody != -1 /*|| (GetOwner() && GetOwner()->IsPlayer())*/) // TODO: Player models check the viewmodel instead of the worldmodel, but setting the bodygroup regardless can cause a crash, so we need a better solution + SetBodygroup( iBody, iSetting ); + else + m_nSkin = iSetting; +} + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::FireNPCBolt( CAI_BaseNPC *pOwner, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + Assert(pOwner); + + QAngle angAiming; + VectorAngles( vecShootDir, angAiming ); + + CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate( vecShootOrigin, angAiming, pOwner ); + + if ( pOwner->GetWaterLevel() == 3 ) + { + pBolt->SetAbsVelocity( vecShootDir * BOLT_WATER_VELOCITY ); + } + else + { + pBolt->SetAbsVelocity( vecShootDir * BOLT_AIR_VELOCITY ); + } + + m_iClip1--; + + SetBolt( 1 ); + + WeaponSound( SINGLE_NPC ); + WeaponSound( SPECIAL2 ); + + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 200, 0.2 ); + + m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 2.5f; + + SetSkin( BOLT_SKIN_GLOW ); + SetChargerState( CHARGER_STATE_DISCHARGE ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::Reload_NPC( bool bPlaySound ) +{ + BaseClass::Reload_NPC( bPlaySound ); + + SetBolt( 0 ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponCrossbow::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCBolt( pOperator->MyNPCPointer(), vecShootOrigin, vecShootDir ); + + //m_bMustReload = true; +} +#endif +#endif + //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. @@ -702,11 +1108,18 @@ bool CWeaponCrossbow::Deploy( void ) { if ( m_iClip1 <= 0 ) { +#ifdef MAPBASE + SetBolt( 1 ); +#endif return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() ); } SetSkin( BOLT_SKIN_GLOW ); +#ifdef MAPBASE + SetBolt( 0 ); +#endif + return BaseClass::Deploy(); } @@ -766,7 +1179,11 @@ void CWeaponCrossbow::CreateChargerEffects( void ) #ifndef CLIENT_DLL CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); +#ifdef MAPBASE + if ( m_hChargerSprite != NULL || pOwner == NULL ) +#else if ( m_hChargerSprite != NULL ) +#endif return; m_hChargerSprite = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE, GetAbsOrigin(), false ); @@ -868,6 +1285,10 @@ void CWeaponCrossbow::SetChargerState( ChargerState_t state ) // Shoot some sparks and draw a beam between the two outer points DoLoadEffect(); + +#ifdef MAPBASE + SetBolt( 0 ); +#endif break; #ifndef CLIENT_DLL @@ -928,7 +1349,6 @@ void CWeaponCrossbow::SetChargerState( ChargerState_t state ) } } -#ifndef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: // Input : *pEvent - @@ -953,14 +1373,27 @@ void CWeaponCrossbow::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombat SetChargerState( CHARGER_STATE_READY ); break; +#if defined(MAPBASE) && !defined(CLIENT_DLL) + case EVENT_WEAPON_SMG1: + { + CAI_BaseNPC *pNPC = pOperator->MyNPCPointer(); + Assert(pNPC); + + Vector vecSrc = pNPC->Weapon_ShootPosition(); + Vector vecAiming = pNPC->GetActualShootTrajectory( vecSrc ); + + FireNPCBolt( pNPC, vecSrc, vecAiming ); + //m_bMustReload = true; + } + break; +#endif + default: BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); break; } } -#endif - //----------------------------------------------------------------------------- // Purpose: Set the desired activity for the weapon and its viewmodel counterpart // Input : iActivity - activity to play diff --git a/src/game/shared/hl2mp/weapon_crowbar.cpp b/src/game/shared/hl2mp/weapon_crowbar.cpp index 55a03d1d691..abd12f7b773 100644 --- a/src/game/shared/hl2mp/weapon_crowbar.cpp +++ b/src/game/shared/hl2mp/weapon_crowbar.cpp @@ -29,6 +29,8 @@ #define CROWBAR_RANGE 75.0f #define CROWBAR_REFIRE 0.4f +ConVar sk_plr_dmg_crowbar ( "sk_plr_dmg_crowbar","0", FCVAR_REPLICATED ); +ConVar sk_npc_dmg_crowbar ( "sk_npc_dmg_crowbar","0", FCVAR_REPLICATED ); //----------------------------------------------------------------------------- // CWeaponCrowbar @@ -45,24 +47,38 @@ END_PREDICTION_DATA() LINK_ENTITY_TO_CLASS( weapon_crowbar, CWeaponCrowbar ); PRECACHE_WEAPON_REGISTER( weapon_crowbar ); -#ifndef CLIENT_DLL - -acttable_t CWeaponCrowbar::m_acttable[] = +acttable_t CWeaponCrowbar::m_acttable[] = { - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, + { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, + { ACT_IDLE, ACT_IDLE_ANGRY_MELEE, false }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false }, +#if 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 + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_MELEE, false }, + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_MELEE, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponCrowbar); -#endif - //----------------------------------------------------------------------------- // Constructor //----------------------------------------------------------------------------- @@ -77,7 +93,10 @@ CWeaponCrowbar::CWeaponCrowbar( void ) //----------------------------------------------------------------------------- float CWeaponCrowbar::GetDamageForActivity( Activity hitActivity ) { - return 25.0f; + if ( ( GetOwner() != NULL ) && ( GetOwner()->IsPlayer() ) ) + return sk_plr_dmg_crowbar.GetFloat(); // 25 + + return sk_npc_dmg_crowbar.GetFloat(); } //----------------------------------------------------------------------------- @@ -101,56 +120,6 @@ void CWeaponCrowbar::AddViewKick( void ) #ifndef CLIENT_DLL -//----------------------------------------------------------------------------- -// Animation event handlers -//----------------------------------------------------------------------------- -void CWeaponCrowbar::HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) -{ - // Trace up or down based on where the enemy is... - // But only if we're basically facing that direction - Vector vecDirection; - AngleVectors( GetAbsAngles(), &vecDirection ); - - Vector vecEnd; - VectorMA( pOperator->Weapon_ShootPosition(), 50, vecDirection, vecEnd ); - CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, - Vector(-16,-16,-16), Vector(36,36,36), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.75 ); - - // did I hit someone? - if ( pHurt ) - { - // play sound - WeaponSound( MELEE_HIT ); - - // Fake a trace impact, so the effects work out like a player's crowbaw - trace_t traceHit; - UTIL_TraceLine( pOperator->Weapon_ShootPosition(), pHurt->GetAbsOrigin(), MASK_SHOT_HULL, pOperator, COLLISION_GROUP_NONE, &traceHit ); - ImpactEffect( traceHit ); - } - else - { - WeaponSound( MELEE_MISS ); - } -} - - -//----------------------------------------------------------------------------- -// Animation event -//----------------------------------------------------------------------------- -void CWeaponCrowbar::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) -{ - switch( pEvent->event ) - { - case EVENT_WEAPON_MELEE_HIT: - HandleAnimEventMeleeHit( pEvent, pOperator ); - break; - - default: - BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); - break; - } -} - //----------------------------------------------------------------------------- // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!) //----------------------------------------------------------------------------- @@ -201,6 +170,71 @@ int CWeaponCrowbar::WeaponMeleeAttack1Condition( float flDot, float flDist ) return COND_CAN_MELEE_ATTACK1; } + +//----------------------------------------------------------------------------- +// Animation event handlers +//----------------------------------------------------------------------------- +void CWeaponCrowbar::HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + // Trace up or down based on where the enemy is... + // But only if we're basically facing that direction + Vector vecDirection; + AngleVectors( GetAbsAngles(), &vecDirection ); + + CBaseEntity *pEnemy = pOperator->MyNPCPointer() ? pOperator->MyNPCPointer()->GetEnemy() : NULL; + if ( pEnemy ) + { + Vector vecDelta; + VectorSubtract( pEnemy->WorldSpaceCenter(), pOperator->Weapon_ShootPosition(), vecDelta ); + VectorNormalize( vecDelta ); + + Vector2D vecDelta2D = vecDelta.AsVector2D(); + Vector2DNormalize( vecDelta2D ); + if ( DotProduct2D( vecDelta2D, vecDirection.AsVector2D() ) > 0.8f ) + { + vecDirection = vecDelta; + } + } + + Vector vecEnd; + VectorMA( pOperator->Weapon_ShootPosition(), 50, vecDirection, vecEnd ); + CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd, + Vector(-16,-16,-16), Vector(36,36,36), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.75 ); + + // did I hit someone? + if ( pHurt ) + { + // play sound + WeaponSound( MELEE_HIT ); + + // Fake a trace impact, so the effects work out like a player's crowbaw + trace_t traceHit; + UTIL_TraceLine( pOperator->Weapon_ShootPosition(), pHurt->GetAbsOrigin(), MASK_SHOT_HULL, pOperator, COLLISION_GROUP_NONE, &traceHit ); + ImpactEffect( traceHit ); + } + else + { + WeaponSound( MELEE_MISS ); + } +} + + +//----------------------------------------------------------------------------- +// Animation event +//----------------------------------------------------------------------------- +void CWeaponCrowbar::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_MELEE_HIT: + HandleAnimEventMeleeHit( pEvent, pOperator ); + break; + + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} #endif diff --git a/src/game/shared/hl2mp/weapon_crowbar.h b/src/game/shared/hl2mp/weapon_crowbar.h index 2cd10fb9c0a..57301cd41c6 100644 --- a/src/game/shared/hl2mp/weapon_crowbar.h +++ b/src/game/shared/hl2mp/weapon_crowbar.h @@ -35,10 +35,7 @@ class CWeaponCrowbar : public CBaseHL2MPBludgeonWeapon DECLARE_NETWORKCLASS(); DECLARE_PREDICTABLE(); - -#ifndef CLIENT_DLL DECLARE_ACTTABLE(); -#endif CWeaponCrowbar(); diff --git a/src/game/shared/hl2mp/weapon_frag.cpp b/src/game/shared/hl2mp/weapon_frag.cpp index 8f4b7b001ed..a86af4bc1b2 100644 --- a/src/game/shared/hl2mp/weapon_frag.cpp +++ b/src/game/shared/hl2mp/weapon_frag.cpp @@ -15,6 +15,7 @@ #include "hl2mp_player.h" #include "te_effect_dispatch.h" #include "grenade_frag.h" + #include "soundent.h" #endif #include "weapon_ar2.h" @@ -59,12 +60,14 @@ class CWeaponFrag: public CBaseHL2MPCombatWeapon bool Deploy( void ); bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); + +#ifndef CLIENT_DLL + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } +#endif bool Reload( void ); -#ifndef CLIENT_DLL void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); -#endif void ThrowGrenade( CBasePlayer *pPlayer ); bool IsPrimed( bool ) { return ( m_AttackPaused != 0 ); } @@ -83,28 +86,44 @@ class CWeaponFrag: public CBaseHL2MPCombatWeapon CWeaponFrag( const CWeaponFrag & ); -#ifndef CLIENT_DLL DECLARE_ACTTABLE(); +#ifndef CLIENT_DLL + DECLARE_DATADESC(); #endif }; #ifndef CLIENT_DLL +BEGIN_DATADESC( CWeaponFrag ) + DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ), + DEFINE_FIELD( m_AttackPaused, FIELD_INTEGER ), + DEFINE_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN ), +END_DATADESC() + +#endif + acttable_t CWeaponFrag::m_acttable[] = { - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_GRENADE, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_GRENADE, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_GRENADE, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_GRENADE, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_GRENADE, false }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_GRENADE, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_GRENADE, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_GRENADE, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_GRENADE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_GRENADE, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_GRENADE, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_GRENADE, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponFrag); -#endif - IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFrag, DT_WeaponFrag ) BEGIN_NETWORK_TABLE( CWeaponFrag, DT_WeaponFrag ) @@ -153,7 +172,6 @@ void CWeaponFrag::Precache( void ) PrecacheScriptSound( "WeaponFrag.Roll" ); } -#ifndef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: // Input : *pEvent - @@ -199,10 +217,27 @@ void CWeaponFrag::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatChar m_flNextPrimaryAttack = gpGlobals->curtime + RETHROW_DELAY; m_flNextSecondaryAttack = gpGlobals->curtime + RETHROW_DELAY; m_flTimeWeaponIdle = FLT_MAX; //NOTE: This is set once the animation has finished up! - } -} + +#ifndef CLIENT_DLL + // Make a sound designed to scare snipers back into their holes! + CBaseCombatCharacter *pOwner = GetOwner(); + + if( pOwner ) + { + Vector vecSrc = pOwner->Weapon_ShootPosition(); + Vector vecDir; + + AngleVectors( pOwner->EyeAngles(), &vecDir ); + + trace_t tr; + + UTIL_TraceLine( vecSrc, vecSrc + vecDir * 1024, MASK_SOLID_BRUSHONLY, pOwner, COLLISION_GROUP_NONE, &tr ); + CSoundEnt::InsertSound( SOUND_DANGER_SNIPERONLY, tr.endpos, 384, 0.2, pOwner ); + } #endif + } +} //----------------------------------------------------------------------------- // Purpose: @@ -488,6 +523,9 @@ void CWeaponFrag::LobGrenade( CBasePlayer *pPlayer ) if ( pGrenade ) { +#ifdef MAPBASE_MP + if ( GetHL2MPWpnData().m_iPlayerDamage > 0 ) +#endif pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage ); pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS ); } @@ -496,7 +534,11 @@ void CWeaponFrag::LobGrenade( CBasePlayer *pPlayer ) WeaponSound( WPN_DOUBLE ); // player "shoot" animation +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#else pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif m_bRedraw = true; } @@ -549,7 +591,11 @@ void CWeaponFrag::RollGrenade( CBasePlayer *pPlayer ) WeaponSound( SPECIAL1 ); // player "shoot" animation +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#else pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif m_bRedraw = true; } diff --git a/src/game/shared/hl2mp/weapon_hl2mpbase.cpp b/src/game/shared/hl2mp/weapon_hl2mpbase.cpp index 15995a5be7d..3a37436e1bb 100644 --- a/src/game/shared/hl2mp/weapon_hl2mpbase.cpp +++ b/src/game/shared/hl2mp/weapon_hl2mpbase.cpp @@ -10,6 +10,10 @@ #include "takedamageinfo.h" #include "ammodef.h" #include "hl2mp_gamerules.h" +#ifdef MAPBASE +#include "mapbase/protagonist_system.h" +#include "eventlist.h" +#endif #ifdef CLIENT_DLL @@ -292,6 +296,68 @@ void CWeaponHL2MPBase::FireBullets( const FireBulletsInfo_t &info ) BaseClass::FireBullets( modinfo ); } +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const char *CWeaponHL2MPBase::GetViewModel( int viewmodelindex ) const +{ + if (GetOwner() && GetOwner()->IsPlayer() && viewmodelindex == 0) + { + const char *pszProtagVM = g_ProtagonistSystem.GetProtagonist_ViewModel( static_cast(GetOwner()), this ); + if (pszProtagVM) + return pszProtagVM; + } + + return BaseClass::GetViewModel( viewmodelindex ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CWeaponHL2MPBase::GetViewmodelFOVOverride() const +{ + if (GetOwner() && GetOwner()->IsPlayer()) + { + float *flVMFOV = g_ProtagonistSystem.GetProtagonist_ViewModelFOV( static_cast(GetOwner()), this ); + if (flVMFOV) + return *flVMFOV; + } + + return BaseClass::GetViewmodelFOVOverride(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponHL2MPBase::UsesHands() const +{ + if (GetOwner() && GetOwner()->IsPlayer()) + { + bool *bProtagUsesHands = g_ProtagonistSystem.GetProtagonist_UsesHands( static_cast(GetOwner()), this ); + if (bProtagUsesHands) + return *bProtagUsesHands; + } + + return BaseClass::UsesHands(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CWeaponHL2MPBase::GetHandRig() const +{ + if (GetOwner() && GetOwner()->IsPlayer()) + { + int *nProtagHandRig = g_ProtagonistSystem.GetProtagonist_HandRig( static_cast(GetOwner()), this ); + if (nProtagHandRig) + return *nProtagHandRig; + } + + return BaseClass::GetHandRig(); +} +#endif + #if defined( CLIENT_DLL ) @@ -299,9 +365,60 @@ void CWeaponHL2MPBase::FireBullets( const FireBulletsInfo_t &info ) #define NUM_MUZZLE_FLASH_TYPES 4 +#ifdef MAPBASE_MP +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEvent - +// *pOperator - +//----------------------------------------------------------------------------- +void CWeaponHL2MPBase::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + if ( (pEvent->type & AE_TYPE_NEWEVENTSYSTEM) && (pEvent->type & AE_TYPE_CLIENT) ) + { + /*if ( pEvent->event == AE_NPC_WEAPON_FIRE ) + { + bool bSecondary = (atoi( pEvent->options ) != 0); + Operator_ForceNPCFire( pOperator, bSecondary ); + return; + } + else*/ if ( pEvent->event == AE_WPN_PLAYWPNSOUND ) + { + int iSnd = GetWeaponSoundFromString(pEvent->options); + if ( iSnd != -1 ) + { + WeaponSound( (WeaponSound_t)iSnd ); + } + } + } + + //DevWarning( 2, "Unhandled animation event %d from %s --> %s\n", pEvent->event, pOperator->GetClassname(), GetClassname() ); +} +#endif + bool CWeaponHL2MPBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ) { +#ifdef MAPBASE_MP + bool bBase = BaseClass::OnFireEvent( pViewModel, origin, angles, event, options ); + + if (!bBase) + { + //If the player is receiving this message, pass it through + C_BasePlayer *pOwner = ToBasePlayer( GetOwner() ); + + if ( pOwner != NULL ) + { + animevent_t animEvent; + animEvent.event = event; + animEvent.options = options; + Operator_HandleAnimEvent( &animEvent, pOwner ); + return true; + } + } + + return bBase; +#else return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options ); +#endif } diff --git a/src/game/shared/hl2mp/weapon_hl2mpbase.h b/src/game/shared/hl2mp/weapon_hl2mpbase.h index 0f21044b1c1..aef902a5119 100644 --- a/src/game/shared/hl2mp/weapon_hl2mpbase.h +++ b/src/game/shared/hl2mp/weapon_hl2mpbase.h @@ -13,6 +13,9 @@ #include "hl2mp_player_shared.h" #include "basecombatweapon_shared.h" #include "hl2mp_weapon_parse.h" +#ifdef MAPBASE_MP +#include "npcevent.h" +#endif #if defined( CLIENT_DLL ) #define CWeaponHL2MPBase C_WeaponHL2MPBase @@ -60,6 +63,13 @@ class CWeaponHL2MPBase : public CBaseCombatWeapon virtual void FireBullets( const FireBulletsInfo_t &info ); virtual void FallInit( void ); + +#ifdef MAPBASE + virtual const char *GetViewModel( int viewmodelindex = 0 ) const; + virtual float GetViewmodelFOVOverride() const; + virtual bool UsesHands( void ) const; + virtual int GetHandRig( void ) const; +#endif public: #if defined( CLIENT_DLL ) @@ -69,6 +79,10 @@ class CWeaponHL2MPBase : public CBaseCombatWeapon virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options ); +#ifdef MAPBASE_MP + virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#endif + #else virtual void Spawn(); diff --git a/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp b/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp index a20cea56b10..0a3baf61227 100644 --- a/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp +++ b/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp @@ -10,6 +10,9 @@ #include "c_hl2mp_player.h" #else #include "hl2mp_player.h" + #ifdef MAPBASE + #include "ai_basenpc.h" + #endif #endif #include "weapon_hl2mpbase_machinegun.h" @@ -109,6 +112,10 @@ void CHL2MPMachineGun::PrimaryAttack( void ) //Factor in the view kick AddViewKick(); + +#if defined(MAPBASE_MP) && !defined(CLIENT_DLL) + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pPlayer, SOUNDENT_CHANNEL_WEAPON ); +#endif if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0) { @@ -118,6 +125,11 @@ void CHL2MPMachineGun::PrimaryAttack( void ) SendWeaponAnim( GetPrimaryAttackActivity() ); pPlayer->SetAnimation( PLAYER_ATTACK1 ); + +#if defined(MAPBASE_MP) && !defined(CLIENT_DLL) + // Register a muzzleflash for the AI + pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 ); +#endif } //----------------------------------------------------------------------------- @@ -132,6 +144,33 @@ void CHL2MPMachineGun::FireBullets( const FireBulletsInfo_t &info ) } } +#if defined(MAPBASE_MP) && !defined(CLIENT_DLL) +//----------------------------------------------------------------------------- +// Purpose: Weapon firing conditions +//----------------------------------------------------------------------------- +int CHL2MPMachineGun::WeaponRangeAttack1Condition( float flDot, float flDist ) +{ + if ( m_iClip1 <=0 ) + { + return COND_NO_PRIMARY_AMMO; + } + else if ( flDist < m_fMinRange1 ) + { + return COND_TOO_CLOSE_TO_ATTACK; + } + else if ( flDist > m_fMaxRange1 ) + { + return COND_TOO_FAR_TO_ATTACK; + } + else if ( flDot < 0.5f ) // UNDONE: Why check this here? Isn't the AI checking this already? + { + return COND_NOT_FACING_ATTACK; + } + + return COND_CAN_RANGE_ATTACK1; +} +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- diff --git a/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h b/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h index e1cdd3245b9..3ebdf384a63 100644 --- a/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h +++ b/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h @@ -45,6 +45,10 @@ class CHL2MPMachineGun : public CBaseHL2MPCombatWeapon // utility function static void DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime ); +#if defined(MAPBASE_MP) && !defined(CLIENT_DLL) + virtual int WeaponRangeAttack1Condition( float flDot, float flDist ); +#endif + private: CHL2MPMachineGun( const CHL2MPMachineGun & ); diff --git a/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp b/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp index 336cfcb7c58..0acac6bd70f 100644 --- a/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp +++ b/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp @@ -16,11 +16,18 @@ #if defined( CLIENT_DLL ) #include "c_hl2mp_player.h" + #include "takedamageinfo.h" + #ifdef MAPBASE_MP + #include "c_ai_basenpc.h" + #endif #else #include "hl2mp_player.h" #include "ndebugoverlay.h" #include "te_effect_dispatch.h" #include "ilagcompensationmanager.h" + #ifdef MAPBASE + #include "ai_basenpc.h" + #endif #endif // memdbgon must be the last include file in a .cpp file!!! @@ -69,6 +76,28 @@ void CBaseHL2MPBludgeonWeapon::Precache( void ) BaseClass::Precache(); } +#if defined(MAPBASE) && !defined(CLIENT_DLL) +int CBaseHL2MPBludgeonWeapon::CapabilitiesGet() +{ + return bits_CAP_WEAPON_MELEE_ATTACK1; +} + + +int CBaseHL2MPBludgeonWeapon::WeaponMeleeAttack1Condition( float flDot, float flDist ) +{ + if (flDist > 64) + { + return COND_TOO_FAR_TO_ATTACK; + } + else if (flDot < 0.7) + { + return COND_NOT_FACING_ATTACK; + } + + return COND_CAN_MELEE_ATTACK1; +} +#endif + //------------------------------------------------------------------------------ // Purpose : Update weapon //------------------------------------------------------------------------------ @@ -79,6 +108,22 @@ void CBaseHL2MPBludgeonWeapon::ItemPostFrame( void ) if ( pOwner == NULL ) return; +#ifdef MAPBASE + if (pOwner->HasSpawnFlags( SF_PLAYER_SUPPRESS_FIRING )) + { + m_bShotDelayed = false; + WeaponIdle(); + return; + } + + // See if we need to fire off our secondary round + if (m_bShotDelayed) + { + if (gpGlobals->curtime > m_flDelayedFire) + DelayedAttack(); + } + else +#endif if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) ) { PrimaryAttack(); @@ -137,6 +182,11 @@ void CBaseHL2MPBludgeonWeapon::Hit( trace_t &traceHit, Activity nHitActivity ) //Do view kick // AddViewKick(); +#if defined(MAPBASE_MP) && !defined(CLIENT_DLL) + //Make sound for the AI + CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, traceHit.endpos, 400, 0.2f, pPlayer ); +#endif + CBaseEntity *pHitEntity = traceHit.m_pEnt; //Apply damage to a hit target @@ -147,7 +197,11 @@ void CBaseHL2MPBludgeonWeapon::Hit( trace_t &traceHit, Activity nHitActivity ) VectorNormalize( hitDirection ); #ifndef CLIENT_DLL +#ifdef MAPBASE + CTakeDamageInfo info( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), GetDamageType() ); +#else CTakeDamageInfo info( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB ); +#endif if( pPlayer && pHitEntity->IsNPC() ) { @@ -303,9 +357,12 @@ void CBaseHL2MPBludgeonWeapon::Swing( int bIsSecondary ) #ifndef CLIENT_DLL // Like bullets, bludgeon traces have to trace against triggers. +#ifdef MAPBASE + CTakeDamageInfo triggerInfo( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), GetDamageType() ); +#else CTakeDamageInfo triggerInfo( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB ); - TraceAttackToTriggers( triggerInfo, traceHit.startpos, traceHit.endpos, vec3_origin ); #endif + TraceAttackToTriggers( triggerInfo, traceHit.startpos, traceHit.endpos, vec3_origin ); if ( traceHit.fraction == 1.0 ) { @@ -334,8 +391,11 @@ void CBaseHL2MPBludgeonWeapon::Swing( int bIsSecondary ) } } } +#endif +#ifndef MAPBASE WeaponSound( SINGLE ); +#endif // ------------------------- // Miss @@ -344,16 +404,23 @@ void CBaseHL2MPBludgeonWeapon::Swing( int bIsSecondary ) { nHitActivity = bIsSecondary ? ACT_VM_MISSCENTER2 : ACT_VM_MISSCENTER; +#ifdef MAPBASE + //Play swing sound + WeaponSound( SINGLE ); +#else // We want to test the first swing again Vector testEnd = swingStart + forward * GetRange(); // See if we happened to hit water ImpactWater( swingStart, testEnd ); +#endif } +#ifndef MAPBASE else { Hit( traceHit, nHitActivity ); } +#endif // Send the anim SendWeaponAnim( nHitActivity ); @@ -363,4 +430,130 @@ void CBaseHL2MPBludgeonWeapon::Swing( int bIsSecondary ) //Setup our next attack times m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate(); m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration(); + +#ifdef MAPBASE + if (GetHitDelay() > 0.f) + { + //Play swing sound + WeaponSound(SINGLE); + + m_flDelayedFire = gpGlobals->curtime + GetHitDelay(); + m_bShotDelayed = true; + } + else + { + if (traceHit.fraction == 1.0f) + { + // We want to test the first swing again + Vector testEnd = swingStart + forward * GetRange(); + + //Play swing sound + WeaponSound(SINGLE); + + // See if we happened to hit water + ImpactWater(swingStart, testEnd); + } + else + { + // Other melee sounds + if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld()) + WeaponSound(MELEE_HIT_WORLD); +#ifndef CLIENT_DLL + else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo)) + WeaponSound(MELEE_MISS); +#endif + else + WeaponSound(MELEE_HIT); + + Hit(traceHit, nHitActivity); + } + } +#endif +} + +#ifdef MAPBASE +void CBaseHL2MPBludgeonWeapon::DelayedAttack(void) +{ + m_bShotDelayed = false; + + trace_t traceHit; + + // Try a ray + CBasePlayer* pOwner = ToBasePlayer(GetOwner()); + if (!pOwner) + return; + + Vector swingStart = pOwner->Weapon_ShootPosition(); + Vector forward; + + pOwner->EyeVectors( &forward, NULL, NULL ); + + Vector swingEnd = swingStart + forward * GetRange(); + UTIL_TraceLine( swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit ); + + if (traceHit.fraction == 1.0) + { + float bludgeonHullRadius = 1.732f * BLUDGEON_HULL_DIM; // hull is +/- 16, so use cuberoot of 2 to determine how big the hull is from center to the corner point + + // Back off by hull "radius" + swingEnd -= forward * bludgeonHullRadius; + + UTIL_TraceHull(swingStart, swingEnd, g_bludgeonMins, g_bludgeonMaxs, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit); + if (traceHit.fraction < 1.0 && traceHit.m_pEnt) + { + Vector vecToTarget = traceHit.m_pEnt->GetAbsOrigin() - swingStart; + VectorNormalize(vecToTarget); + + float dot = vecToTarget.Dot(forward); + + // YWB: Make sure they are sort of facing the guy at least... + if (dot < 0.70721f) + { + // Force amiss + traceHit.fraction = 1.0f; + } + else + { + ChooseIntersectionPointAndActivity(traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner); + } + } + } + + if (traceHit.fraction == 1.0f) + { + // We want to test the first swing again + Vector testEnd = swingStart + forward * GetRange(); + + // See if we happened to hit water + ImpactWater(swingStart, testEnd); + } + else + { +#ifndef CLIENT_DLL + CTakeDamageInfo triggerInfo(GetOwner(), GetOwner(), GetDamageForActivity(GetActivity()), GetDamageType()); + triggerInfo.SetDamagePosition(traceHit.startpos); + triggerInfo.SetDamageForce(forward); +#endif + + // Other melee sounds + if (traceHit.m_pEnt && traceHit.m_pEnt->IsWorld()) + WeaponSound(MELEE_HIT_WORLD); +#ifndef CLIENT_DLL + else if (traceHit.m_pEnt && !traceHit.m_pEnt->PassesDamageFilter(triggerInfo)) + WeaponSound(MELEE_MISS); +#endif + else + WeaponSound(MELEE_HIT); + + Hit(traceHit, GetActivity()); + } +} + +bool CBaseHL2MPBludgeonWeapon::CanHolster(void) +{ + if (m_bShotDelayed) + return false; + + return BaseClass::CanHolster(); } +#endif // MAPBASE diff --git a/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h b/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h index 54160dfddd6..679a0d6c5d0 100644 --- a/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h +++ b/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h @@ -40,6 +40,9 @@ class CBaseHL2MPBludgeonWeapon : public CBaseHL2MPCombatWeapon //Attack functions virtual void PrimaryAttack( void ); virtual void SecondaryAttack( void ); +#ifdef MAPBASE + void DelayedAttack(void); +#endif // MAPBASE virtual void ItemPostFrame( void ); @@ -51,6 +54,17 @@ class CBaseHL2MPBludgeonWeapon : public CBaseHL2MPCombatWeapon virtual float GetRange( void ) { return 32.0f; } virtual float GetDamageForActivity( Activity hitActivity ) { return 1.0f; } +#ifdef MAPBASE +#ifndef CLIENT_DLL + virtual int CapabilitiesGet( void ); + virtual int WeaponMeleeAttack1Condition( float flDot, float flDist ); +#endif + + virtual int GetDamageType() { return DMG_CLUB; } + virtual float GetHitDelay() { return 0.f; } + virtual bool CanHolster( void ); +#endif // MAPBASE + CBaseHL2MPBludgeonWeapon( const CBaseHL2MPBludgeonWeapon & ); virtual bool PlayFleshyHittySoundOnHit() const { return false; } @@ -63,6 +77,11 @@ class CBaseHL2MPBludgeonWeapon : public CBaseHL2MPCombatWeapon void Swing( int bIsSecondary ); void Hit( trace_t &traceHit, Activity nHitActivity ); Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner ); + +#ifdef MAPBASE + float m_flDelayedFire; + bool m_bShotDelayed; +#endif // MAPBASE }; -#endif +#endif \ No newline at end of file diff --git a/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp b/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp index c697adaac66..84943d8a435 100644 --- a/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp +++ b/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp @@ -12,6 +12,10 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +ConVar hl2mp_allow_weapon_lower( "hl2mp_allow_weapon_lower", "0", FCVAR_REPLICATED ); +#endif + LINK_ENTITY_TO_CLASS( basehl2mpcombatweapon, CBaseHL2MPCombatWeapon ); IMPLEMENT_NETWORKCLASS_ALIASED( BaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon ) @@ -82,6 +86,20 @@ void CBaseHL2MPCombatWeapon::ItemHolsterFrame( void ) } } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CBaseHL2MPCombatWeapon::CanLower() +{ +#ifdef MAPBASE + if ( !hl2mp_allow_weapon_lower.GetBool() ) + return BaseClass::CanLower(); +#endif + + if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE ) + return false; + return true; +} + //----------------------------------------------------------------------------- // Purpose: Drops the weapon into a lowered pose // Output : Returns true on success, false on failure. @@ -308,6 +326,14 @@ float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void ) g_lateralBob = speed*0.005f; g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle); g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f ); + +#ifdef MAPBASE + if (GetBobScale() != 1.0f) + { + //g_verticalBob *= GetBobScale(); + g_lateralBob *= GetBobScale(); + } +#endif //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!) return 0.0f; diff --git a/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h b/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h index e8469326319..ba5c34fa2d2 100644 --- a/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h +++ b/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h @@ -17,6 +17,12 @@ #define CBaseHL2MPCombatWeapon C_BaseHL2MPCombatWeapon #endif +#ifdef MAPBASE + // Alias for migrated HL2 weapons + #undef CBaseHLCombatWeapon + #define CBaseHLCombatWeapon CBaseHL2MPCombatWeapon +#endif + class CBaseHL2MPCombatWeapon : public CWeaponHL2MPBase { #if !defined( CLIENT_DLL ) @@ -33,6 +39,7 @@ class CBaseHL2MPCombatWeapon : public CWeaponHL2MPBase virtual bool WeaponShouldBeLowered( void ); virtual bool Ready( void ); + virtual bool CanLower( void ); virtual bool Lower( void ); virtual bool Deploy( void ); virtual bool Holster( CBaseCombatWeapon *pSwitchingTo ); diff --git a/src/game/shared/hl2mp/weapon_pistol.cpp b/src/game/shared/hl2mp/weapon_pistol.cpp index cbaadfffec6..c69f1e0255b 100644 --- a/src/game/shared/hl2mp/weapon_pistol.cpp +++ b/src/game/shared/hl2mp/weapon_pistol.cpp @@ -7,6 +7,7 @@ #include "cbase.h" #include "npcevent.h" #include "in_buttons.h" +#include "ai_basenpc_shared.h" #ifdef CLIENT_DLL #include "c_hl2mp_player.h" @@ -50,12 +51,25 @@ class CWeaponPistol : public CBaseHL2MPCombatWeapon void UpdatePenaltyTime( void ); +#ifndef CLIENT_DLL + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); + + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } +#endif Activity GetPrimaryAttackActivity( void ); virtual bool Reload( void ); virtual const Vector& GetBulletSpread( void ) { + // Handle NPCs first + static Vector npcCone = VECTOR_CONE_5DEGREES; + if ( GetOwner() && GetOwner()->IsNPC() ) + return npcCone; + static Vector cone; float ramp = RemapValClamped( m_flAccuracyPenalty, @@ -84,9 +98,17 @@ class CWeaponPistol : public CBaseHL2MPCombatWeapon { 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(); #ifndef CLIENT_DLL - DECLARE_ACTTABLE(); + DECLARE_DATADESC(); #endif private: @@ -128,16 +150,149 @@ LINK_ENTITY_TO_CLASS( weapon_pistol, CWeaponPistol ); PRECACHE_WEAPON_REGISTER( weapon_pistol ); #ifndef CLIENT_DLL -acttable_t CWeaponPistol::m_acttable[] = +BEGIN_DATADESC( CWeaponPistol ) + + DEFINE_FIELD( m_flSoonestPrimaryAttack, FIELD_TIME ), + DEFINE_FIELD( m_flLastAttackTime, FIELD_TIME ), + DEFINE_FIELD( m_flAccuracyPenalty, FIELD_FLOAT ), //NOTENOTE: This is NOT tracking game time + DEFINE_FIELD( m_nNumShotsFired, FIELD_INTEGER ), + +END_DATADESC() +#endif + +acttable_t CWeaponPistol::m_acttable[] = { - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false }, - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, false }, + { ACT_IDLE, ACT_IDLE_PISTOL, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_PISTOL, true }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, true }, + { ACT_RELOAD, ACT_RELOAD_PISTOL, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_PISTOL, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_PISTOL, true }, + { ACT_GESTURE_RANGE_ATTACK1, ACT_GESTURE_RANGE_ATTACK_PISTOL,true }, + { ACT_RELOAD_LOW, ACT_RELOAD_PISTOL_LOW, false }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_PISTOL_LOW, false }, + { ACT_COVER_LOW, ACT_COVER_PISTOL_LOW, false }, + { ACT_RANGE_AIM_LOW, ACT_RANGE_AIM_PISTOL_LOW, false }, + { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_PISTOL, false }, + { ACT_WALK, ACT_WALK_PISTOL, false }, + { ACT_RUN, ACT_RUN_PISTOL, false }, + +#ifdef MAPBASE + // + // Activities ported from weapon_alyxgun below + // + +#if 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 }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_PISTOL, false },//always aims + { ACT_IDLE_STEALTH, ACT_IDLE_STEALTH_PISTOL, false }, + + { ACT_WALK_RELAXED, ACT_WALK, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_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, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_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, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_ANGRY_PISTOL, 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, 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, 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 +#endif + + // Crouch activities + { ACT_CROUCHIDLE_STIMULATED, ACT_CROUCHIDLE_STIMULATED, false }, + { ACT_CROUCHIDLE_AIM_STIMULATED,ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + { ACT_CROUCHIDLE_AGITATED, ACT_RANGE_AIM_PISTOL_LOW, false },//always aims + + // Readiness translations + { ACT_READINESS_RELAXED_TO_STIMULATED, ACT_READINESS_PISTOL_RELAXED_TO_STIMULATED, false }, + { 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 }, +#endif + +#if 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 + +#if 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 + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_PISTOL, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_PISTOL, false }, +#endif +#endif }; @@ -156,8 +311,6 @@ int GetPistolActtableCount() } #endif -#endif - //----------------------------------------------------------------------------- // Purpose: Constructor //----------------------------------------------------------------------------- @@ -182,6 +335,79 @@ void CWeaponPistol::Precache( void ) BaseClass::Precache(); } +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CWeaponPistol::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_PISTOL_FIRE: + { + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + Assert( npc != NULL ); + + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + +#ifdef MAPBASE + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +#else + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + WeaponSound( SINGLE_NPC ); + pOperator->FireBullets( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 2 ); + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; +#endif + } + break; + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponPistol::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + WeaponSound( SINGLE_NPC ); + + FireBulletsInfo_t info( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType ); + info.m_iTracerFreq = 2; + + pOperator->FireBullets( info ); + + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: Some things need this. (e.g. the new Force(X)Fire inputs or blindfire actbusy) +//----------------------------------------------------------------------------- +void CWeaponPistol::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +} +#endif +#endif //----------------------------------------------------------------------------- // Purpose: @@ -211,6 +437,9 @@ void CWeaponPistol::PrimaryAttack( void ) m_flLastAttackTime = gpGlobals->curtime; m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME; +#ifndef CLIENT_DLL + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), SOUNDENT_VOLUME_PISTOL, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON ); +#endif CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); @@ -331,6 +560,10 @@ bool CWeaponPistol::Reload( void ) return fRet; } +#ifdef MAPBASE +ConVar weapon_pistol_upwards_viewkick( "weapon_pistol_upwards_viewkick", "0" ); +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -343,7 +576,13 @@ void CWeaponPistol::AddViewKick( void ) QAngle viewPunch; - viewPunch.x = SharedRandomFloat( "pistolpax", 0.25f, 0.5f ); +#ifdef MAPBASE + bool bUpwardsViewKick = weapon_pistol_upwards_viewkick.GetBool(); +#else + bool bUpwardsViewKick = false; +#endif + + viewPunch.x = bUpwardsViewKick ? SharedRandomFloat( "pistolpax", -0.5f, -0.25f ) : SharedRandomFloat( "pistolpax", 0.25f, 0.5f ); viewPunch.y = SharedRandomFloat( "pistolpay", -.6f, .6f ); viewPunch.z = 0.0f; diff --git a/src/game/shared/hl2mp/weapon_rpg.cpp b/src/game/shared/hl2mp/weapon_rpg.cpp index 1859958e3ca..29410bd5d3d 100644 --- a/src/game/shared/hl2mp/weapon_rpg.cpp +++ b/src/game/shared/hl2mp/weapon_rpg.cpp @@ -8,6 +8,7 @@ #include "npcevent.h" #include "in_buttons.h" #include "weapon_rpg.h" +#include "ai_basenpc_shared.h" #ifdef CLIENT_DLL #include "c_hl2mp_player.h" @@ -45,6 +46,12 @@ const char *g_pLaserDotThink = "LaserThinkContext"; static ConVar sk_apc_missile_damage("sk_apc_missile_damage", "15"); +ConVar rpg_missle_use_custom_detonators( "rpg_missle_use_custom_detonators", "1" ); +#ifdef MAPBASE +ConVar weapon_rpg_use_old_behavior( "weapon_rpg_use_old_behavior", "0" ); +ConVar weapon_rpg_fire_rate( "weapon_rpg_fire_rate", "4.0" ); +#endif + #define APC_MISSILE_DAMAGE sk_apc_missile_damage.GetFloat() #endif @@ -127,6 +134,7 @@ BEGIN_DATADESC( CMissile ) DEFINE_FIELD( m_flMarkDeadTime, FIELD_TIME ), DEFINE_FIELD( m_flGracePeriodEndsAt, FIELD_TIME ), DEFINE_FIELD( m_flDamage, FIELD_FLOAT ), + DEFINE_FIELD( m_bCreateDangerSounds, FIELD_BOOLEAN ), // Function Pointers DEFINE_FUNCTION( MissileTouch ), @@ -147,6 +155,7 @@ class CWeaponRPG; CMissile::CMissile() { m_hRocketTrail = NULL; + m_bCreateDangerSounds = false; } CMissile::~CMissile() @@ -412,7 +421,11 @@ void CMissile::MissileTouch( CBaseEntity *pOther ) // Don't touch triggers (but DO hit weapons) if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON ) - return; + { + // Some NPCs are triggers that can take damage (like antlion grubs). We should hit them. + if ( ( pOther->m_takedamage == DAMAGE_NO ) || ( pOther->m_takedamage == DAMAGE_EVENTS_ONLY ) ) + return; + } Explode(); } @@ -471,8 +484,11 @@ void CMissile::IgniteThink( void ) { CBasePlayer *pPlayer = ToBasePlayer( m_hOwner->GetOwner() ); - color32 white = { 255,225,205,64 }; - UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN ); + if ( pPlayer ) + { + color32 white = { 255,225,205,64 }; + UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN ); + } } CreateSmokeTrail(); @@ -584,6 +600,50 @@ void CMissile::SeekThink( void ) } } + if( hl2_episodic.GetBool() ) + { + if( flBestDist <= ( GetAbsVelocity().Length() * 2.5f ) && FVisible( pBestDot->GetAbsOrigin() ) ) + { + // Scare targets + CSoundEnt::InsertSound( SOUND_DANGER, pBestDot->GetAbsOrigin(), CMissile::EXPLOSION_RADIUS, 0.2f, pBestDot, SOUNDENT_CHANNEL_REPEATED_DANGER, NULL ); + } + } + + if ( rpg_missle_use_custom_detonators.GetBool() ) + { + for ( int i = gm_CustomDetonators.Count() - 1; i >=0; --i ) + { + CustomDetonator_t &detonator = gm_CustomDetonators[i]; + if ( !detonator.hEntity ) + { + gm_CustomDetonators.FastRemove( i ); + } + else + { + const Vector &vPos = detonator.hEntity->CollisionProp()->WorldSpaceCenter(); + if ( detonator.halfHeight > 0 ) + { + if ( fabsf( vPos.z - GetAbsOrigin().z ) < detonator.halfHeight ) + { + if ( ( GetAbsOrigin().AsVector2D() - vPos.AsVector2D() ).LengthSqr() < detonator.radiusSq ) + { + Explode(); + return; + } + } + } + else + { + if ( ( GetAbsOrigin() - vPos ).LengthSqr() < detonator.radiusSq ) + { + Explode(); + return; + } + } + } + } + } + //If we have a dot target if ( pBestDot == NULL ) { @@ -606,6 +666,16 @@ void CMissile::SeekThink( void ) VectorSubtract( targetPos, GetAbsOrigin(), vTargetDir ); float flDist = VectorNormalize( vTargetDir ); + if( pLaserDot->GetTargetEntity() != NULL && flDist <= 240.0f && hl2_episodic.GetBool() ) + { + // Prevent the missile circling the Strider like a Halo in ep1_c17_06. If the missile gets within 20 + // feet of a Strider, tighten up the turn speed of the missile so it can break the halo and strike. (sjb 4/27/2006) + if( pLaserDot->GetTargetEntity()->ClassMatches( "npc_strider" ) ) + { + flHomingSpeed *= 1.75f; + } + } + Vector vDir = GetAbsVelocity(); float flSpeed = VectorNormalize( vDir ); Vector vNewVelocity = vDir; @@ -643,6 +713,17 @@ void CMissile::SeekThink( void ) // Think as soon as possible SetNextThink( gpGlobals->curtime ); + +#ifdef HL2_EPISODIC + + if ( m_bCreateDangerSounds == true ) + { + trace_t tr; + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + GetAbsVelocity() * 0.5, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); + + CSoundEnt::InsertSound( SOUND_DANGER, tr.endpos, 100, 0.2, this, SOUNDENT_CHANNEL_REPEATED_DANGER ); + } +#endif } @@ -672,6 +753,33 @@ CMissile *CMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, ed } +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +CUtlVector CMissile::gm_CustomDetonators; + +void CMissile::AddCustomDetonator( CBaseEntity *pEntity, float radius, float height ) +{ + int i = gm_CustomDetonators.AddToTail(); + gm_CustomDetonators[i].hEntity = pEntity; + gm_CustomDetonators[i].radiusSq = Square( radius ); + gm_CustomDetonators[i].halfHeight = height * 0.5f; +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CMissile::RemoveCustomDetonator( CBaseEntity *pEntity ) +{ + for ( int i = 0; i < gm_CustomDetonators.Count(); i++ ) + { + if ( gm_CustomDetonators[i].hEntity == pEntity ) + { + gm_CustomDetonators.FastRemove( i ); + break; + } + } +} + //----------------------------------------------------------------------------- // This entity is used to create little force boxes that the helicopter @@ -868,6 +976,7 @@ BEGIN_DATADESC( CAPCMissile ) DEFINE_THINKFUNC( BeginSeekThink ), DEFINE_THINKFUNC( AugerStartThink ), DEFINE_THINKFUNC( ExplodeThink ), + DEFINE_THINKFUNC( APCSeekThink ), DEFINE_FUNCTION( APCMissileTouch ), @@ -912,6 +1021,7 @@ void CAPCMissile::Init() CreateSmokeTrail(); SetTouch( &CAPCMissile::APCMissileTouch ); m_flLastHomingSpeed = APC_HOMING_SPEED; + CreateDangerSounds( true ); } @@ -984,10 +1094,43 @@ void CAPCMissile::ExplodeDelay( float flDelay ) void CAPCMissile::BeginSeekThink( void ) { RemoveSolidFlags( FSOLID_NOT_SOLID ); +#ifdef HL2MP SetThink( &CAPCMissile::SeekThink ); +#else + SetThink( &CAPCMissile::APCSeekThink ); +#endif SetNextThink( gpGlobals->curtime ); } +void CAPCMissile::APCSeekThink( void ) +{ + BaseClass::SeekThink(); + + bool bFoundDot = false; + + //If we can't find a dot to follow around then just send me wherever I'm facing so I can blow up in peace. + for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext ) + { + if ( !pEnt->IsOn() ) + continue; + + if ( pEnt->GetOwnerEntity() != GetOwnerEntity() ) + continue; + + bFoundDot = true; + } + + if ( bFoundDot == false ) + { + Vector vDir = GetAbsVelocity(); + VectorNormalize ( vDir ); + + SetAbsVelocity( vDir * 800 ); + + SetThink( NULL ); + } +} + void CAPCMissile::ExplodeThink() { DoExplosion(); @@ -1036,8 +1179,11 @@ void CAPCMissile::DoExplosion( void ) } else { - ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), - APC_MISSILE_DAMAGE, 100, true, 20000 ); +#ifdef HL2_EPISODIC + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), this, APC_MISSILE_DAMAGE, 100, true, 20000 ); +#else + ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), APC_MISSILE_DAMAGE, 100, true, 20000 ); +#endif } } @@ -1254,6 +1400,19 @@ void CAPCMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActua // RPG //============================================================================= +#ifndef CLIENT_DLL +BEGIN_DATADESC( CWeaponRPG ) + + DEFINE_FIELD( m_bInitialStateUpdate,FIELD_BOOLEAN ), + DEFINE_FIELD( m_bGuiding, FIELD_BOOLEAN ), + DEFINE_FIELD( m_vecNPCLaserDot, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_hLaserDot, FIELD_EHANDLE ), + DEFINE_FIELD( m_hMissile, FIELD_EHANDLE ), + DEFINE_FIELD( m_bHideGuiding, FIELD_BOOLEAN ), + +END_DATADESC() +#endif + LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG ); PRECACHE_WEAPON_REGISTER(weapon_rpg); @@ -1305,22 +1464,61 @@ END_PREDICTION_DATA() #endif -#ifndef CLIENT_DLL acttable_t CWeaponRPG::m_acttable[] = { - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_RPG, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_RPG, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_RPG, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_RPG, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_RPG, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_RPG, false }, - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, false }, -}; + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, true }, +#if 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 -IMPLEMENT_ACTTABLE(CWeaponRPG); +#ifdef MAPBASE + // Readiness activities should not be required + { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, false }, + { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_RPG, false }, +#else + { ACT_IDLE_RELAXED, ACT_IDLE_RPG_RELAXED, true }, + { ACT_IDLE_STIMULATED, ACT_IDLE_ANGRY_RPG, true }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_RPG, true }, +#endif + + { ACT_IDLE, ACT_IDLE_RPG, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_RPG, true }, + { ACT_WALK, ACT_WALK_RPG, true }, + { ACT_WALK_CROUCH, ACT_WALK_CROUCH_RPG, true }, + { ACT_RUN, ACT_RUN_RPG, true }, + { ACT_RUN_CROUCH, ACT_RUN_CROUCH_RPG, true }, + { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true }, + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RPG, false }, + { ACT_DISARM, ACT_DISARM_RPG, false }, +#endif + +#if 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 +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_RPG, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_RPG, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_RPG, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_RPG, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_RPG, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_RPG, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_RPG, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_RPG, false }, +#endif #endif +}; + +IMPLEMENT_ACTTABLE(CWeaponRPG); //----------------------------------------------------------------------------- // Purpose: @@ -1394,6 +1592,128 @@ void CWeaponRPG::Activate( void ) } } +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pEvent - +// *pOperator - +//----------------------------------------------------------------------------- +void CWeaponRPG::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_SMG1: + { + if ( m_hMissile != NULL ) + return; + + Vector muzzlePoint; + QAngle vecAngles; + + muzzlePoint = GetOwner()->Weapon_ShootPosition(); + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + Assert( npc != NULL ); + + Vector vecShootDir = npc->GetActualShootTrajectory( muzzlePoint ); + + // look for a better launch location + Vector altLaunchPoint; + if (GetAttachment( "missile", altLaunchPoint )) + { + // check to see if it's relativly free + trace_t tr; + AI_TraceHull( altLaunchPoint, altLaunchPoint + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr ); + + if( tr.fraction == 1.0) + { + muzzlePoint = altLaunchPoint; + } + } + + VectorAngles( vecShootDir, vecAngles ); + + CMissile *pMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() ); + pMissile->m_hOwner = this; + + // NPCs always get a grace period + pMissile->SetGracePeriod( 0.5 ); + + m_hMissile = pMissile; + + pOperator->DoMuzzleFlash(); + + WeaponSound( SINGLE_NPC ); + + // Make sure our laserdot is off + m_bGuiding = false; + +#ifndef CLIENT_DLL + if ( m_hLaserDot ) + { + m_hLaserDot->TurnOff(); + } +#endif + } + break; + + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponRPG::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + if ( m_hMissile != NULL ) + return; + + Vector muzzlePoint, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), muzzlePoint, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + + // look for a better launch location + Vector altLaunchPoint; + if (GetAttachment( "missile", altLaunchPoint )) + { + // check to see if it's relativly free + trace_t tr; + AI_TraceHull( altLaunchPoint, altLaunchPoint + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr ); + + if( tr.fraction == 1.0) + { + muzzlePoint = altLaunchPoint; + } + } + + CMissile *pMissile = CMissile::Create( muzzlePoint, angShootDir, pOperator->edict() ); + pMissile->m_hOwner = this; + + // NPCs always get a grace period + pMissile->SetGracePeriod( 0.5 ); + + m_hMissile = pMissile; + + pOperator->DoMuzzleFlash(); + + WeaponSound( SINGLE_NPC ); + + // Make sure our laserdot is off + m_bGuiding = false; + + if ( m_hLaserDot ) + { + m_hLaserDot->TurnOff(); + } +} +#endif +#endif + //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- @@ -1423,12 +1743,6 @@ bool CWeaponRPG::WeaponShouldBeLowered( void ) //----------------------------------------------------------------------------- void CWeaponRPG::PrimaryAttack( void ) { - // Only the player fires this way so we can cast - CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); - - if (!pPlayer) - return; - // Can't have an active missile out if ( m_hMissile != NULL ) return; @@ -1472,14 +1786,53 @@ void CWeaponRPG::PrimaryAttack( void ) pMissile->SetDamage( GetHL2MPWpnData().m_iPlayerDamage ); m_hMissile = pMissile; + + // Register a muzzleflash for the AI + pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 ); #endif DecrementAmmo( GetOwner() ); + SendWeaponAnim( ACT_VM_PRIMARYATTACK ); WeaponSound( SINGLE ); // player "shoot" animation - pPlayer->SetAnimation( PLAYER_ATTACK1 ); + pOwner->SetAnimation( PLAYER_ATTACK1 ); + +#ifndef CLIENT_DLL + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON ); + + // Check to see if we should trigger any RPG firing triggers + int iCount = g_hWeaponFireTriggers.Count(); + for ( int i = 0; i < iCount; i++ ) + { + if ( g_hWeaponFireTriggers[i]->IsTouching( pOwner ) ) + { + if ( FClassnameIs( g_hWeaponFireTriggers[i], "trigger_rpgfire" ) ) + { + g_hWeaponFireTriggers[i]->ActivateMultiTrigger( pOwner ); + } + } + } +#endif + +#ifdef HL2_EPISODIC + if( hl2_episodic.GetBool() ) + { + CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); + int nAIs = g_AI_Manager.NumAIs(); + + string_t iszStriderClassname = AllocPooledString( "npc_strider" ); + + for ( int i = 0; i < nAIs; i++ ) + { + if( ppAIs[ i ]->m_iClassname == iszStriderClassname ) + { + ppAIs[ i ]->DispatchInteraction( g_interactionPlayerLaunchedRPG, NULL, m_hMissile ); + } + } + } +#endif } //----------------------------------------------------------------------------- @@ -1573,6 +1926,7 @@ void CWeaponRPG::ItemPostFrame( void ) } } +#ifndef CLIENT_DLL //----------------------------------------------------------------------------- // Purpose: // Output : Vector @@ -1597,6 +1951,18 @@ Vector CWeaponRPG::GetLaserPosition( void ) //----------------------------------------------------------------------------- void CWeaponRPG::UpdateNPCLaserPosition( const Vector &vecTarget ) { + CreateLaserPointer(); + // Turn the laserdot on + m_bGuiding = true; + m_hLaserDot->TurnOn(); + + Vector muzzlePoint = GetOwner()->Weapon_ShootPosition(); + Vector vecDir = (vecTarget - muzzlePoint); + VectorNormalize( vecDir ); + vecDir = muzzlePoint + ( vecDir * MAX_TRACE_LENGTH ); + UpdateLaserPosition( muzzlePoint, vecDir ); + + SetNPCLaserPosition( vecTarget ); } @@ -1605,6 +1971,8 @@ void CWeaponRPG::UpdateNPCLaserPosition( const Vector &vecTarget ) //----------------------------------------------------------------------------- void CWeaponRPG::SetNPCLaserPosition( const Vector &vecTarget ) { + m_vecNPCLaserDot = vecTarget; + //NDebugOverlay::Box( m_vecNPCLaserDot, -Vector(10,10,10), Vector(10,10,10), 255,0,0, 8, 3 ); } //----------------------------------------------------------------------------- @@ -1612,8 +1980,9 @@ void CWeaponRPG::SetNPCLaserPosition( const Vector &vecTarget ) //----------------------------------------------------------------------------- const Vector &CWeaponRPG::GetNPCLaserPosition( void ) { - return vec3_origin; + return m_vecNPCLaserDot; } +#endif //----------------------------------------------------------------------------- // Purpose: @@ -1752,14 +2121,14 @@ void CWeaponRPG::UpdateLaserPosition( Vector vecMuzzlePos, Vector vecEndPos ) trace_t tr; // Trace out for the endpoint - UTIL_TraceLine( vecMuzzlePos, vecEndPos, (MASK_SHOT & ~CONTENTS_WINDOW), GetOwner(), COLLISION_GROUP_NONE, &tr ); + UTIL_TraceLine( vecMuzzlePos, vecEndPos, (MASK_SHOT & ~CONTENTS_WINDOW), this, COLLISION_GROUP_NONE, &tr ); // Move the laser sprite if ( m_hLaserDot != NULL ) { Vector laserPos = tr.endpos; m_hLaserDot->SetLaserPosition( laserPos, tr.plane.normal ); - + if ( tr.DidHitNonWorldEntity() ) { CBaseEntity *pHit = tr.m_pEnt; @@ -1838,7 +2207,131 @@ bool CWeaponRPG::Reload( void ) return true; } -#ifdef CLIENT_DLL +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +bool CWeaponRPG::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) +{ + bool bResult = BaseClass::WeaponLOSCondition( ownerPos, targetPos, bSetConditions ); + + if( bResult ) + { + CAI_BaseNPC* npcOwner = GetOwner()->MyNPCPointer(); + + if( npcOwner ) + { + trace_t tr; + + Vector vecRelativeShootPosition; + VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition ); + Vector vecMuzzle = ownerPos + vecRelativeShootPosition; + Vector vecShootDir = npcOwner->GetActualShootTrajectory( vecMuzzle ); + + // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out. +#ifdef MAPBASE + // Oh, and don't collide with ourselves or our owner. That would be stupid. + if (!weapon_rpg_use_old_behavior.GetBool()) + { + CTraceFilterSkipTwoEntities pTraceFilter( this, GetOwner(), COLLISION_GROUP_NONE ); + AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, &pTraceFilter, &tr ); + } + else + { +#endif + AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr ); +#ifdef MAPBASE + } +#endif + + if( tr.fraction != 1.0f ) + bResult = false; + } + } + + return bResult; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : flDot - +// flDist - +// Output : int +//----------------------------------------------------------------------------- +int CWeaponRPG::WeaponRangeAttack1Condition( float flDot, float flDist ) +{ + if ( m_hMissile != NULL ) + return 0; + + // Ignore vertical distance when doing our RPG distance calculations + CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); + if ( pNPC ) + { + CBaseEntity *pEnemy = pNPC->GetEnemy(); + Vector vecToTarget = (pEnemy->GetAbsOrigin() - pNPC->GetAbsOrigin()); + vecToTarget.z = 0; + flDist = vecToTarget.Length(); + } + + if ( flDist < MIN( m_fMinRange1, m_fMinRange2 ) ) + return COND_TOO_CLOSE_TO_ATTACK; + + if ( m_flNextPrimaryAttack > gpGlobals->curtime ) + return 0; + + // See if there's anyone in the way! + CAI_BaseNPC *pOwner = GetOwner()->MyNPCPointer(); + ASSERT( pOwner != NULL ); + + if( pOwner ) + { + // Make sure I don't shoot the world! + trace_t tr; + + Vector vecMuzzle = pOwner->Weapon_ShootPosition(); + Vector vecShootDir = pOwner->GetActualShootTrajectory( vecMuzzle ); + + // Make sure I have a good 10 feet of wide clearance in front, or I'll blow my teeth out. +#ifdef MAPBASE + // Oh, and don't collide with ourselves or our owner. That would be stupid. + if (!weapon_rpg_use_old_behavior.GetBool()) + { + CTraceFilterSkipTwoEntities pTraceFilter( this, GetOwner(), COLLISION_GROUP_NONE ); + AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, &pTraceFilter, &tr ); + } + else + { +#endif + AI_TraceHull( vecMuzzle, vecMuzzle + vecShootDir * (10.0f*12.0f), Vector( -24, -24, -24 ), Vector( 24, 24, 24 ), MASK_NPCSOLID, NULL, &tr ); +#ifdef MAPBASE + } +#endif + + if( tr.fraction != 1.0 ) + { + return COND_WEAPON_SIGHT_OCCLUDED; + } + } + + return COND_CAN_RANGE_ATTACK1; +} + +#ifdef MAPBASE +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CWeaponRPG::SupportsBackupActivity(Activity activity) +{ + // NPCs shouldn't use their SMG activities to aim and fire RPGs while running. + if (activity == ACT_RUN_AIM || + activity == ACT_WALK_AIM || + activity == ACT_RUN_CROUCH_AIM || + activity == ACT_WALK_CROUCH_AIM) + return false; + + return true; +} +#endif +#else #define RPG_MUZZLE_ATTACHMENT 1 #define RPG_GUIDE_ATTACHMENT 2 diff --git a/src/game/shared/hl2mp/weapon_rpg.h b/src/game/shared/hl2mp/weapon_rpg.h index bbd6f2dd243..2af0ba08012 100644 --- a/src/game/shared/hl2mp/weapon_rpg.h +++ b/src/game/shared/hl2mp/weapon_rpg.h @@ -37,6 +37,8 @@ class CMissile : public CBaseCombatCharacter DECLARE_CLASS( CMissile, CBaseCombatCharacter ); public: + static const int EXPLOSION_RADIUS = 200; + CMissile(); ~CMissile(); @@ -70,6 +72,11 @@ class CMissile : public CBaseCombatCharacter static CMissile *Create( const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner ); + void CreateDangerSounds( bool bState ){ m_bCreateDangerSounds = bState; } + + static void AddCustomDetonator( CBaseEntity *pEntity, float radius, float height = -1 ); + static void RemoveCustomDetonator( CBaseEntity *pEntity ); + protected: virtual void DoExplosion(); virtual void ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed ); @@ -86,8 +93,18 @@ class CMissile : public CBaseCombatCharacter float m_flMarkDeadTime; float m_flDamage; + struct CustomDetonator_t + { + EHANDLE hEntity; + float radiusSq; + float halfHeight; + }; + + static CUtlVector gm_CustomDetonators; + private: float m_flGracePeriodEndsAt; + bool m_bCreateDangerSounds; DECLARE_DATADESC(); }; @@ -125,6 +142,8 @@ class CAPCMissile : public CMissile void AimAtSpecificTarget( CBaseEntity *pTarget ); void SetGuidanceHint( const char *pHintName ); + void APCSeekThink( void ); + CAPCMissile *m_pNext; protected: @@ -154,6 +173,9 @@ class CAPCMissile : public CMissile //----------------------------------------------------------------------------- CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDirection, float flAngle ); +#ifdef MAPBASE +extern ConVar weapon_rpg_fire_rate; +#endif #endif //----------------------------------------------------------------------------- @@ -164,9 +186,9 @@ CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDir #define CWeaponRPG C_WeaponRPG #endif -class CWeaponRPG : public CBaseHL2MPCombatWeapon +class CWeaponRPG : public CBaseHLCombatWeapon { - DECLARE_CLASS( CWeaponRPG, CBaseHL2MPCombatWeapon ); + DECLARE_CLASS( CWeaponRPG, CBaseHLCombatWeapon ); public: CWeaponRPG(); @@ -178,7 +200,11 @@ class CWeaponRPG : public CBaseHL2MPCombatWeapon void Precache( void ); void PrimaryAttack( void ); +#if defined(MAPBASE) && !defined(CLIENT_DLL) + virtual float GetFireRate( void ) { return weapon_rpg_fire_rate.GetFloat(); }; +#else virtual float GetFireRate( void ) { return 1; }; +#endif void ItemPostFrame( void ); void Activate( void ); @@ -199,6 +225,15 @@ class CWeaponRPG : public CBaseHL2MPCombatWeapon float GetMinRestTime() { return 4.0; } float GetMaxRestTime() { return 4.0; } + bool WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ); + int WeaponRangeAttack1Condition( float flDot, float flDist ); + +#ifndef CLIENT_DLL + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#ifdef MAPBASE + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); +#endif +#endif void StartGuiding( void ); void StopGuiding( void ); void ToggleGuiding( void ); @@ -214,10 +249,19 @@ class CWeaponRPG : public CBaseHL2MPCombatWeapon void UpdateLaserPosition( Vector vecMuzzlePos = vec3_origin, Vector vecEndPos = vec3_origin ); Vector GetLaserPosition( void ); +#ifndef CLIENT_DLL + // NPC RPG users cheat and directly set the laser pointer's origin void UpdateNPCLaserPosition( const Vector &vecTarget ); void SetNPCLaserPosition( const Vector &vecTarget ); const Vector &GetNPCLaserPosition( void ); + + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + +#ifdef MAPBASE + bool SupportsBackupActivity( Activity activity ); +#endif +#endif #ifdef CLIENT_DLL @@ -240,10 +284,17 @@ class CWeaponRPG : public CBaseHL2MPCombatWeapon #endif //CLIENT_DLL + virtual const Vector& GetBulletSpread( void ) + { + static Vector cone = VECTOR_CONE_3DEGREES; + return cone; + } + CBaseEntity *GetMissile( void ) { return m_hMissile; } -#ifndef CLIENT_DLL DECLARE_ACTTABLE(); +#ifndef CLIENT_DLL + DECLARE_DATADESC(); #endif protected: @@ -256,6 +307,7 @@ class CWeaponRPG : public CBaseHL2MPCombatWeapon CNetworkVar( Vector, m_vecLaserDot ); #ifndef CLIENT_DLL + Vector m_vecNPCLaserDot; CHandle m_hLaserDot; #endif diff --git a/src/game/shared/hl2mp/weapon_shotgun.cpp b/src/game/shared/hl2mp/weapon_shotgun.cpp index b2f62fb018b..798e037cbb0 100644 --- a/src/game/shared/hl2mp/weapon_shotgun.cpp +++ b/src/game/shared/hl2mp/weapon_shotgun.cpp @@ -7,6 +7,7 @@ #include "cbase.h" #include "npcevent.h" #include "in_buttons.h" +#include "ai_basenpc_shared.h" #ifdef CLIENT_DLL #include "c_hl2mp_player.h" @@ -22,6 +23,10 @@ extern ConVar sk_auto_reload_time; extern ConVar sk_plr_num_shotgun_pellets; +#ifdef MAPBASE +extern ConVar sk_plr_num_shotgun_pellets_double; +extern ConVar sk_npc_num_shotgun_pellets; +#endif class CWeaponShotgun : public CBaseHL2MPCombatWeapon { @@ -38,15 +43,35 @@ class CWeaponShotgun : public CBaseHL2MPCombatWeapon CNetworkVar( bool, m_bDelayedReload ); // Reload when finished pump public: +#ifndef CLIENT_DLL + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } +#endif + virtual const Vector& GetBulletSpread( void ) { static Vector cone = VECTOR_CONE_10DEGREES; + +#ifndef CLIENT_DLL + static Vector vitalAllyCone = VECTOR_CONE_3DEGREES; + if( GetOwner() && (GetOwner()->Classify() == CLASS_PLAYER_ALLY_VITAL) ) + { + // Give Alyx's shotgun blasts more a more directed punch. She needs + // to be at least as deadly as she would be with her pistol to stay interesting (sjb) + return vitalAllyCone; + } +#endif + return cone; } virtual int GetMinBurst() { return 1; } virtual int GetMaxBurst() { return 3; } + virtual float GetMinRestTime(); + virtual float GetMaxRestTime(); + + virtual float GetFireRate( void ); + bool StartReload( void ); bool Reload( void ); void FillClip( void ); @@ -59,10 +84,16 @@ class CWeaponShotgun : public CBaseHL2MPCombatWeapon void PrimaryAttack( void ); void SecondaryAttack( void ); void DryFire( void ); - virtual float GetFireRate( void ) { return 0.7; }; #ifndef CLIENT_DLL + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#endif + DECLARE_ACTTABLE(); +#ifndef CLIENT_DLL + DECLARE_DATADESC(); #endif CWeaponShotgun(void); @@ -100,16 +131,140 @@ LINK_ENTITY_TO_CLASS( weapon_shotgun, CWeaponShotgun ); PRECACHE_WEAPON_REGISTER(weapon_shotgun); #ifndef CLIENT_DLL +BEGIN_DATADESC( CWeaponShotgun ) + + DEFINE_FIELD( m_bNeedPump, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bDelayedFire1, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bDelayedFire2, FIELD_BOOLEAN ), + DEFINE_FIELD( m_bDelayedReload, FIELD_BOOLEAN ), + +END_DATADESC() +#endif + acttable_t CWeaponShotgun::m_acttable[] = { - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SHOTGUN, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SHOTGUN, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SHOTGUN, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SHOTGUN, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SHOTGUN, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SHOTGUN, false }, - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, false }, +#if 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 }, + { ACT_RELOAD, ACT_RELOAD_SHOTGUN, false }, + { ACT_WALK, ACT_WALK_RIFLE, 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_SHOTGUN_AGITATED, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, 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_RIFLE, 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 }, +#endif + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_SHOTGUN, true }, + { ACT_DISARM, ACT_DISARM_SHOTGUN, true }, +#endif + +#if 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 + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SHOTGUN, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SHOTGUN, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SHOTGUN, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SHOTGUN, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SHOTGUN, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SHOTGUN, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_SHOTGUN, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_SHOTGUN, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponShotgun); @@ -127,8 +282,129 @@ int GetShotgunActtableCount() } #endif +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +// Input : *pOperator - +//----------------------------------------------------------------------------- +void CWeaponShotgun::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, bool bUseWeaponAngles ) +{ + Vector vecShootOrigin, vecShootDir; + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + Assert( npc != NULL ); + WeaponSound( SINGLE_NPC ); + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; + + if ( bUseWeaponAngles ) + { + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + } + else + { + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + } + +#ifdef MAPBASE + FireBulletsInfo_t info( sk_npc_num_shotgun_pellets.GetInt(), vecShootOrigin, vecShootDir, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType ); + info.m_iTracerFreq = 0; + + pOperator->FireBullets( info ); +#else + pOperator->FireBullets( 8, vecShootOrigin, vecShootDir, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0 ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponShotgun::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + FireNPCPrimaryAttack( pOperator, true ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CWeaponShotgun::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_SHOTGUN_FIRE: + { + FireNPCPrimaryAttack( pOperator, false ); + } + break; + + default: + CBaseCombatWeapon::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} +#endif + + +//----------------------------------------------------------------------------- +// Purpose: When we shipped HL2, the shotgun weapon did not override the +// BaseCombatWeapon default rest time of 0.3 to 0.6 seconds. When +// NPC's fight from a stationary position, their animation events +// govern when they fire so the rate of fire is specified by the +// animation. When NPC's move-and-shoot, the rate of fire is +// specifically controlled by the shot regulator, so it's imporant +// that GetMinRestTime and GetMaxRestTime are implemented and provide +// reasonable defaults for the weapon. To address difficulty concerns, +// we are going to fix the combine's rate of shotgun fire in episodic. +// This change will not affect Alyx using a shotgun in EP1. (sjb) +//----------------------------------------------------------------------------- +float CWeaponShotgun::GetMinRestTime() +{ +#ifndef CLIENT_DLL + if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE ) + { + return 1.2f; + } +#endif + + return BaseClass::GetMinRestTime(); +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +float CWeaponShotgun::GetMaxRestTime() +{ +#ifndef CLIENT_DLL + if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE ) + { + return 1.5f; + } +#endif + + return BaseClass::GetMaxRestTime(); +} + +//----------------------------------------------------------------------------- +// Purpose: Time between successive shots in a burst. Also returned for EP2 +// with an eye to not messing up Alyx in EP1. +//----------------------------------------------------------------------------- +float CWeaponShotgun::GetFireRate() +{ +#ifndef CLIENT_DLL + if( hl2_episodic.GetBool() && GetOwner() && GetOwner()->Classify() == CLASS_COMBINE ) + { + return 0.8f; + } #endif + return 0.7; +} //----------------------------------------------------------------------------- // Purpose: Override so only reload one shell at a time @@ -165,6 +441,13 @@ bool CWeaponShotgun::StartReload( void ) pOwner->m_flNextAttack = gpGlobals->curtime; m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +#ifdef MAPBASE + if ( pOwner->IsPlayer() ) + { + static_cast(pOwner)->SetAnimation( PLAYER_RELOAD ); + } +#endif + m_bInReload = true; return true; } @@ -321,7 +604,11 @@ void CWeaponShotgun::PrimaryAttack( void ) SendWeaponAnim( ACT_VM_PRIMARYATTACK ); // Don't fire again until fire animation has completed +#ifdef MAPBASE + m_flNextPrimaryAttack = gpGlobals->curtime + GetViewModelSequenceDuration(); +#else m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +#endif m_iClip1 -= 1; // player "shoot" animation @@ -330,11 +617,19 @@ void CWeaponShotgun::PrimaryAttack( void ) Vector vecSrc = pPlayer->Weapon_ShootPosition( ); Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); +#ifndef CLIENT_DLL + pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 1.0 ); +#endif + +#ifdef HL2MP // TODO: Integrate sk_plr_num_shotgun_pellets FireBulletsInfo_t info( 7, vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType ); info.m_pAttacker = pPlayer; // Fire the bullets, and force the first shot to be perfectly accuracy pPlayer->FireBullets( info ); +#else + pPlayer->FireBullets( sk_plr_num_shotgun_pellets.GetInt(), vecSrc, vecAiming, GetBulletSpread(), MAX_TRACE_LENGTH, m_iPrimaryAmmoType, 0, -1, -1, 0, NULL, true, true ); +#endif QAngle punch; punch.Init( SharedRandomFloat( "shotgunpax", -2, -1 ), SharedRandomFloat( "shotgunpay", -2, 2 ), 0 ); @@ -373,11 +668,21 @@ void CWeaponShotgun::SecondaryAttack( void ) SendWeaponAnim( ACT_VM_SECONDARYATTACK ); // Don't fire again until fire animation has completed +#ifdef MAPBASE + m_flNextPrimaryAttack = gpGlobals->curtime + GetViewModelSequenceDuration(); +#else m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); +#endif m_iClip1 -= 2; // Shotgun uses same clip for primary and secondary attacks // player "shoot" animation +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#else pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif + + Vector vecSrc = pPlayer->Weapon_ShootPosition(); Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES ); diff --git a/src/game/shared/hl2mp/weapon_slam.cpp b/src/game/shared/hl2mp/weapon_slam.cpp index 2499b51a30d..f16a196c5f7 100644 --- a/src/game/shared/hl2mp/weapon_slam.cpp +++ b/src/game/shared/hl2mp/weapon_slam.cpp @@ -104,8 +104,9 @@ BEGIN_DATADESC( CWeapon_SLAM ) DEFINE_FUNCTION( SlamTouch ), END_DATADESC() +#endif -acttable_t CWeapon_SLAM::m_acttable[] = +acttable_t CWeapon_SLAM::m_acttable[] = { { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SLAM, false }, @@ -121,8 +122,7 @@ acttable_t CWeapon_SLAM::m_acttable[] = #endif }; -IMPLEMENT_ACTTABLE(CWeapon_SLAM); -#endif +IMPLEMENT_ACTTABLE( CWeapon_SLAM ); void CWeapon_SLAM::Spawn( ) @@ -495,12 +495,12 @@ void CWeapon_SLAM::StartTripmineAttach( void ) //----------------------------------------------------------------------------- void CWeapon_SLAM::SatchelThrow( void ) { -#ifndef CLIENT_DLL m_bThrowSatchel = false; // Only the player fires this way so we can cast CBasePlayer *pPlayer = ToBasePlayer( GetOwner() ); +#ifndef CLIENT_DLL Vector vecSrc = pPlayer->WorldSpaceCenter(); Vector vecFacing = pPlayer->BodyDirection3D( ); vecSrc = vecSrc + vecFacing * 18.0; @@ -529,12 +529,11 @@ void CWeapon_SLAM::SatchelThrow( void ) pSatchel->m_bIsLive = true; pSatchel->m_pMyWeaponSLAM = this; } +#endif pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType ); pPlayer->SetAnimation( PLAYER_ATTACK1 ); -#endif - // Play throw sound EmitSound( "Weapon_SLAM.SatchelThrow" ); } @@ -577,7 +576,6 @@ void CWeapon_SLAM::StartSatchelThrow( void ) //----------------------------------------------------------------------------- void CWeapon_SLAM::SatchelAttach( void ) { -#ifndef CLIENT_DLL CBaseCombatCharacter *pOwner = GetOwner(); if (!pOwner) { @@ -585,7 +583,8 @@ void CWeapon_SLAM::SatchelAttach( void ) } m_bAttachSatchel = false; - + +#ifndef CLIENT_DLL Vector vecSrc = pOwner->Weapon_ShootPosition( ); Vector vecAiming = pOwner->BodyDirection2D( ); @@ -625,15 +624,24 @@ void CWeapon_SLAM::SatchelAttach( void ) //----------------------------------------------------------------------------- void CWeapon_SLAM::StartSatchelAttach( void ) { -#ifndef CLIENT_DLL +#if !defined(CLIENT_DLL) || defined(MAPBASE_MP) CBaseCombatCharacter *pOwner = GetOwner(); if (!pOwner) { return; } +#ifdef CLIENT_DLL + Vector vecAiming; + + // Must compromise due to no body direction or Weapon_ShootPosition() on client + QAngle angEyeAngles = pOwner->EyeAngles(); + AngleVectors( angEyeAngles, &vecAiming ); + Vector vecSrc = Vector( 0, 32, 0 ); +#else Vector vecSrc = pOwner->Weapon_ShootPosition( ); Vector vecAiming = pOwner->BodyDirection2D( ); +#endif trace_t tr; diff --git a/src/game/shared/hl2mp/weapon_slam.h b/src/game/shared/hl2mp/weapon_slam.h index bf7286122df..c7e52afbf95 100644 --- a/src/game/shared/hl2mp/weapon_slam.h +++ b/src/game/shared/hl2mp/weapon_slam.h @@ -87,8 +87,8 @@ class CWeapon_SLAM : public CBaseHL2MPCombatWeapon CWeapon_SLAM(); -#ifndef CLIENT_DLL DECLARE_ACTTABLE(); +#ifndef CLIENT_DLL DECLARE_DATADESC(); #endif diff --git a/src/game/shared/hl2mp/weapon_smg1.cpp b/src/game/shared/hl2mp/weapon_smg1.cpp index 4d644496cca..7faf76f38a8 100644 --- a/src/game/shared/hl2mp/weapon_smg1.cpp +++ b/src/game/shared/hl2mp/weapon_smg1.cpp @@ -7,16 +7,20 @@ #include "cbase.h" #include "npcevent.h" #include "in_buttons.h" +#include "ai_basenpc_shared.h" #ifdef CLIENT_DLL #include "c_hl2mp_player.h" #else #include "grenade_ar2.h" #include "hl2mp_player.h" + #include "ai_memory.h" + #include "soundent.h" + #include "rumble_shared.h" + #include "gamestats.h" #include "basegrenade_shared.h" #endif -#include "weapon_hl2mpbase.h" #include "weapon_hl2mpbase_machinegun.h" #ifdef CLIENT_DLL @@ -26,7 +30,13 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" +#ifdef MAPBASE +extern ConVar sk_npc_dmg_smg1_grenade; +extern ConVar sk_plr_dmg_smg1_grenade; +#define SMG1_GRENADE_DAMAGE sk_plr_dmg_smg1_grenade.GetFloat() +#else #define SMG1_GRENADE_DAMAGE 100.0f +#endif #define SMG1_GRENADE_RADIUS 250.0f class CWeaponSMG1 : public CHL2MPMachineGun @@ -51,6 +61,10 @@ class CWeaponSMG1 : public CHL2MPMachineGun bool Reload( void ); float GetFireRate( void ) { return 0.075f; } // 13.3hz +#ifndef CLIENT_DLL + int CapabilitiesGet( void ) { return bits_CAP_WEAPON_RANGE_ATTACK1; } + int WeaponRangeAttack2Condition( float flDot, float flDist ); +#endif Activity GetPrimaryAttackActivity( void ); virtual const Vector& GetBulletSpread( void ) @@ -62,7 +76,14 @@ class CWeaponSMG1 : public CHL2MPMachineGun const WeaponProficiencyInfo_t *GetProficiencyValues(); #ifndef CLIENT_DLL + void FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ); + void Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ); + void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ); +#endif + DECLARE_ACTTABLE(); +#ifndef CLIENT_DLL + DECLARE_DATADESC(); #endif protected: @@ -86,16 +107,94 @@ LINK_ENTITY_TO_CLASS( weapon_smg1, CWeaponSMG1 ); PRECACHE_WEAPON_REGISTER(weapon_smg1); #ifndef CLIENT_DLL +BEGIN_DATADESC( CWeaponSMG1 ) + + DEFINE_FIELD( m_vecTossVelocity, FIELD_VECTOR ), + DEFINE_FIELD( m_flNextGrenadeCheck, FIELD_TIME ), + +END_DATADESC() +#endif + acttable_t CWeaponSMG1::m_acttable[] = { - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SMG1, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SMG1, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SMG1, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SMG1, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG1, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SMG1, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SMG1, false }, - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, false }, + { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, true }, + { ACT_RELOAD, ACT_RELOAD_SMG1, true }, + { ACT_IDLE, ACT_IDLE_SMG1, true }, + { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_SMG1, true }, + + { ACT_WALK, ACT_WALK_RIFLE, true }, + { ACT_WALK_AIM, ACT_WALK_AIM_RIFLE, true }, + +// Readiness activities (not aiming) + { ACT_IDLE_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_STIMULATED, ACT_IDLE_SMG1_STIMULATED, false }, + { ACT_IDLE_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_STIMULATED, ACT_WALK_RIFLE_STIMULATED, false }, + { ACT_WALK_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_STIMULATED, ACT_RUN_RIFLE_STIMULATED, false }, + { ACT_RUN_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims + +// Readiness activities (aiming) + { ACT_IDLE_AIM_RELAXED, ACT_IDLE_SMG1_RELAXED, false },//never aims + { ACT_IDLE_AIM_STIMULATED, ACT_IDLE_AIM_RIFLE_STIMULATED, false }, + { ACT_IDLE_AIM_AGITATED, ACT_IDLE_ANGRY_SMG1, false },//always aims + + { ACT_WALK_AIM_RELAXED, ACT_WALK_RIFLE_RELAXED, false },//never aims + { ACT_WALK_AIM_STIMULATED, ACT_WALK_AIM_RIFLE_STIMULATED, false }, + { ACT_WALK_AIM_AGITATED, ACT_WALK_AIM_RIFLE, false },//always aims + + { ACT_RUN_AIM_RELAXED, ACT_RUN_RIFLE_RELAXED, false },//never aims + { ACT_RUN_AIM_STIMULATED, ACT_RUN_AIM_RIFLE_STIMULATED, false }, + { ACT_RUN_AIM_AGITATED, ACT_RUN_AIM_RIFLE, false },//always aims +//End readiness activities + + { 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 }, + { ACT_RUN, ACT_RUN_RIFLE, true }, + { ACT_RUN_AIM, ACT_RUN_AIM_RIFLE, 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_SMG1, true }, + { ACT_RANGE_ATTACK1_LOW, ACT_RANGE_ATTACK_SMG1_LOW, true }, + { ACT_COVER_LOW, ACT_COVER_SMG1_LOW, false }, + { 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 }, + +#if EXPANDED_HL2_WEAPON_ACTIVITIES + { ACT_ARM, ACT_ARM_RIFLE, false }, + { ACT_DISARM, ACT_DISARM_RIFLE, false }, +#endif + +#if 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 + +#ifdef MAPBASE + // HL2:DM activities (for third-person animations in SP) + { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SMG1, false }, + { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SMG1, false }, + { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SMG1, false }, + { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SMG1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG1, false }, + { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SMG1, false }, + { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SMG1, false }, +#if EXPANDED_HL2DM_ACTIVITIES + { ACT_HL2MP_WALK, ACT_HL2MP_WALK_SMG1, false }, + { ACT_HL2MP_GESTURE_RANGE_ATTACK2, ACT_HL2MP_GESTURE_RANGE_ATTACK2_SMG1, false }, +#endif +#endif }; IMPLEMENT_ACTTABLE(CWeaponSMG1); @@ -112,7 +211,6 @@ int GetSMG1ActtableCount() return ARRAYSIZE(CWeaponSMG1::m_acttable); } #endif -#endif //========================================================= CWeaponSMG1::CWeaponSMG1( ) @@ -138,11 +236,170 @@ void CWeaponSMG1::Precache( void ) //----------------------------------------------------------------------------- void CWeaponSMG1::Equip( CBaseCombatCharacter *pOwner ) { - m_fMaxRange1 = 1400; +#ifndef CLIENT_DLL + if( pOwner->Classify() == CLASS_PLAYER_ALLY ) + { + m_fMaxRange1 = 3000; + } + else +#endif + { + m_fMaxRange1 = 1400; + } BaseClass::Equip( pOwner ); } +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponSMG1::FireNPCPrimaryAttack( CBaseCombatCharacter *pOperator, Vector &vecShootOrigin, Vector &vecShootDir ) +{ + // FIXME: use the returned number of bullets to account for >10hz firerate + WeaponSoundRealtime( SINGLE_NPC ); + + CSoundEnt::InsertSound( SOUND_COMBAT|SOUND_CONTEXT_GUNFIRE, pOperator->GetAbsOrigin(), SOUNDENT_VOLUME_MACHINEGUN, 0.2, pOperator, SOUNDENT_CHANNEL_WEAPON, pOperator->GetEnemy() ); + + FireBulletsInfo_t info( 1, vecShootOrigin, vecShootDir, VECTOR_CONE_PRECALCULATED, MAX_TRACE_LENGTH, m_iPrimaryAmmoType ); + info.m_iTracerFreq = 2; + + pOperator->FireBullets( info ); + + pOperator->DoMuzzleFlash(); + m_iClip1 = m_iClip1 - 1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponSMG1::Operator_ForceNPCFire( CBaseCombatCharacter *pOperator, bool bSecondary ) +{ + // Ensure we have enough rounds in the clip + m_iClip1++; + + Vector vecShootOrigin, vecShootDir; + QAngle angShootDir; + GetAttachment( LookupAttachment( "muzzle" ), vecShootOrigin, angShootDir ); + AngleVectors( angShootDir, &vecShootDir ); + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); +} + +#ifdef MAPBASE +float GetCurrentGravity( void ); +#endif + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CWeaponSMG1::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) +{ + switch( pEvent->event ) + { + case EVENT_WEAPON_SMG1: + { + Vector vecShootOrigin, vecShootDir; + QAngle angDiscard; + + // Support old style attachment point firing + if ((pEvent->options == NULL) || (pEvent->options[0] == '\0') || (!pOperator->GetAttachment(pEvent->options, vecShootOrigin, angDiscard))) + { + vecShootOrigin = pOperator->Weapon_ShootPosition(); + } + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + Assert( npc != NULL ); + vecShootDir = npc->GetActualShootTrajectory( vecShootOrigin ); + + FireNPCPrimaryAttack( pOperator, vecShootOrigin, vecShootDir ); + } + break; + +#ifdef MAPBASE + case EVENT_WEAPON_AR2_ALTFIRE: + { + WeaponSound( WPN_DOUBLE ); + + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + if (!npc) + return; + + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetShootEnemyDir( vecShootOrigin ); + + Vector vecTarget = npc->GetAltFireTarget(); + Vector vecThrow; + if (vecTarget == vec3_origin) + AngleVectors( npc->EyeAngles(), &vecThrow ); // Not much else to do, unfortunately + else + { + // Because this is happening right now, we can't "VecCheckThrow" and can only "VecDoThrow", you know what I mean? + // ...Anyway, this borrows from that so we'll never return vec3_origin. + //vecThrow = VecCheckThrow( this, vecShootOrigin, vecTarget, 600.0, 0.5 ); + + vecThrow = (vecTarget - vecShootOrigin); + + // throw at a constant time + float time = vecThrow.Length() / 600.0; + vecThrow = vecThrow * (1.0 / time); + + // adjust upward toss to compensate for gravity loss + vecThrow.z += (GetCurrentGravity() * 0.5) * time * 0.5; + } + + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecShootOrigin, vec3_angle, npc ); + pGrenade->SetAbsVelocity( vecThrow ); + pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); + + pGrenade->SetThrower( npc ); + + pGrenade->SetGravity(0.5); // lower gravity since grenade is aerodynamic and engine doesn't know it. + + pGrenade->SetDamage(sk_npc_dmg_smg1_grenade.GetFloat()); + + variant_t var; + var.SetEntity(pGrenade); + npc->FireNamedOutput("OnThrowGrenade", var, pGrenade, npc); + } + break; +#else + /*//FIXME: Re-enable + case EVENT_WEAPON_AR2_GRENADE: + { + CAI_BaseNPC *npc = pOperator->MyNPCPointer(); + + Vector vecShootOrigin, vecShootDir; + vecShootOrigin = pOperator->Weapon_ShootPosition(); + vecShootDir = npc->GetShootEnemyDir( vecShootOrigin ); + + Vector vecThrow = m_vecTossVelocity; + + CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecShootOrigin, vec3_angle, npc ); + pGrenade->SetAbsVelocity( vecThrow ); + pGrenade->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) ); + pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY ); + pGrenade->m_hOwner = npc; + pGrenade->m_pMyWeaponAR2 = this; + pGrenade->SetDamage(sk_npc_dmg_ar2_grenade.GetFloat()); + + // FIXME: arrgg ,this is hard coded into the weapon??? + m_flNextGrenadeCheck = gpGlobals->curtime + 6;// wait six seconds before even looking again to see if a grenade can be thrown. + + m_iClip2--; + } + break; + */ +#endif + + default: + BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); + break; + } +} +#endif + //----------------------------------------------------------------------------- // Purpose: // Output : Activity @@ -246,8 +503,16 @@ void CWeaponSMG1::SecondaryAttack( void ) SendWeaponAnim( ACT_VM_SECONDARYATTACK ); +#ifndef CLIENT_DLL + CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 1000, 0.2, GetOwner(), SOUNDENT_CHANNEL_WEAPON ); +#endif + // player "shoot" animation +#ifdef MAPBASE + pPlayer->SetAnimation( PLAYER_ATTACK2 ); +#else pPlayer->SetAnimation( PLAYER_ATTACK1 ); +#endif // Decrease ammo pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType ); @@ -260,7 +525,122 @@ void CWeaponSMG1::SecondaryAttack( void ) // misyl: Stop dryfire taking over if we have 1 ammo left. m_flNextEmptySoundTime = gpGlobals->curtime + 1.0f; + +#ifndef CLIENT_DLL + // Register a muzzleflash for the AI. + pPlayer->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 ); +#endif +} + +#define COMBINE_MIN_GRENADE_CLEAR_DIST 256 + +#ifndef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: +// Input : flDot - +// flDist - +// Output : int +//----------------------------------------------------------------------------- +int CWeaponSMG1::WeaponRangeAttack2Condition( float flDot, float flDist ) +{ + CAI_BaseNPC *npcOwner = GetOwner()->MyNPCPointer(); + + return COND_NONE; + +/* + // -------------------------------------------------------- + // Assume things haven't changed too much since last time + // -------------------------------------------------------- + if (gpGlobals->curtime < m_flNextGrenadeCheck ) + return m_lastGrenadeCondition; +*/ + + // ----------------------- + // If moving, don't check. + // ----------------------- + if ( npcOwner->IsMoving()) + return COND_NONE; + + CBaseEntity *pEnemy = npcOwner->GetEnemy(); + + if (!pEnemy) + return COND_NONE; + + Vector vecEnemyLKP = npcOwner->GetEnemyLKP(); + if ( !( pEnemy->GetFlags() & FL_ONGROUND ) && pEnemy->GetWaterLevel() == 0 && vecEnemyLKP.z > (GetAbsOrigin().z + WorldAlignMaxs().z) ) + { + //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to + // be grenaded. + // don't throw grenades at anything that isn't on the ground! + return COND_NONE; + } + + // -------------------------------------- + // Get target vector + // -------------------------------------- + Vector vecTarget; + if (random->RandomInt(0,1)) + { + // magically know where they are + vecTarget = pEnemy->WorldSpaceCenter(); + } + else + { + // toss it to where you last saw them + vecTarget = vecEnemyLKP; + } + // vecTarget = m_vecEnemyLKP + (pEnemy->BodyTarget( GetLocalOrigin() ) - pEnemy->GetLocalOrigin()); + // estimate position + // vecTarget = vecTarget + pEnemy->m_vecVelocity * 2; + + + if ( ( vecTarget - npcOwner->GetLocalOrigin() ).Length2D() <= COMBINE_MIN_GRENADE_CLEAR_DIST ) + { + // crap, I don't want to blow myself up + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return (COND_NONE); + } + + // --------------------------------------------------------------------- + // Are any friendlies near the intended grenade impact area? + // --------------------------------------------------------------------- + CBaseEntity *pTarget = NULL; + + while ( ( pTarget = gEntList.FindEntityInSphere( pTarget, vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST ) ) != NULL ) + { + //Check to see if the default relationship is hatred, and if so intensify that + if ( npcOwner->IRelationType( pTarget ) == D_LI ) + { + // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return (COND_WEAPON_BLOCKED_BY_FRIEND); + } + } + + // --------------------------------------------------------------------- + // Check that throw is legal and clear + // --------------------------------------------------------------------- + // FIXME: speed is based on difficulty... + + Vector vecToss = VecCheckThrow( this, npcOwner->GetLocalOrigin() + Vector(0,0,60), vecTarget, 600.0, 0.5 ); + if ( vecToss != vec3_origin ) + { + m_vecTossVelocity = vecToss; + + // don't check again for a while. + // JAY: HL1 keeps checking - test? + //m_flNextGrenadeCheck = gpGlobals->curtime; + m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second. + return COND_CAN_RANGE_ATTACK2; + } + else + { + // don't check again for a while. + m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. + return COND_WEAPON_SIGHT_OCCLUDED; + } } +#endif //----------------------------------------------------------------------------- const WeaponProficiencyInfo_t *CWeaponSMG1::GetProficiencyValues() diff --git a/src/game/shared/hl2mp/weapon_stunstick.cpp b/src/game/shared/hl2mp/weapon_stunstick.cpp index a71ed475f97..2ca3dbcba09 100644 --- a/src/game/shared/hl2mp/weapon_stunstick.cpp +++ b/src/game/shared/hl2mp/weapon_stunstick.cpp @@ -66,20 +66,8 @@ LINK_ENTITY_TO_CLASS( weapon_stunstick, CWeaponStunStick ); PRECACHE_WEAPON_REGISTER( weapon_stunstick ); -#ifndef CLIENT_DLL - acttable_t CWeaponStunStick::m_acttable[] = { -#ifdef HL2MP - { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, - { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false }, - { ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false }, - { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false }, - { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false }, - { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false }, - { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false }, - { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false }, -#endif { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true }, { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, true }, #if EXPANDED_HL2_WEAPON_ACTIVITIES @@ -107,8 +95,6 @@ acttable_t CWeaponStunStick::m_acttable[] = IMPLEMENT_ACTTABLE(CWeaponStunStick); -#endif - //----------------------------------------------------------------------------- // Constructor @@ -410,11 +396,31 @@ void CWeaponStunStick::SetStunState( bool state ) //FIXME: END - Move to client-side - EmitSound( "Weapon_StunStick.Activate" ); +#ifdef MAPBASE + // Support for weapon sound alternative + if (*GetShootSound( SPECIAL1 )) + { + WeaponSound( SPECIAL1 ); + } + else +#endif + { + EmitSound( "Weapon_StunStick.Activate" ); + } } else { - EmitSound( "Weapon_StunStick.Deactivate" ); +#ifdef MAPBASE + // Support for weapon sound alternative + if (*GetShootSound( SPECIAL2 )) + { + WeaponSound( SPECIAL2 ); + } + else +#endif + { + EmitSound( "Weapon_StunStick.Deactivate" ); + } } } @@ -424,9 +430,12 @@ void CWeaponStunStick::SetStunState( bool state ) //----------------------------------------------------------------------------- bool CWeaponStunStick::Deploy( void ) { + if ( BaseClass::Deploy() == false ) + return false; + SetStunState( true ); - return BaseClass::Deploy(); + return true; } //----------------------------------------------------------------------------- diff --git a/src/game/shared/hl2mp/weapon_stunstick.h b/src/game/shared/hl2mp/weapon_stunstick.h index b1bffc5e607..6b7f8834e80 100644 --- a/src/game/shared/hl2mp/weapon_stunstick.h +++ b/src/game/shared/hl2mp/weapon_stunstick.h @@ -13,9 +13,11 @@ #ifdef HL2MP #include "weapon_hl2mpbasebasebludgeon.h" +#define CBaseHLBludgeonWeapon CBaseHL2MPBludgeonWeapon #else #ifdef CLIENT_DLL #include "c_basehlcombatweapon.h" +#define CBaseHLBludgeonWeapon C_BaseHLBludgeonWeapon #else #include "basebludgeonweapon.h" #endif @@ -30,9 +32,6 @@ #ifdef CLIENT_DLL #define CWeaponStunStick C_WeaponStunStick -#ifndef HL2MP -#define CBaseHLBludgeonWeapon C_BaseHLBludgeonWeapon -#endif #endif #ifndef HL2MP @@ -51,10 +50,7 @@ class CWeaponStunStick : public CBaseHL2MPBludgeonWeapon DECLARE_NETWORKCLASS(); DECLARE_PREDICTABLE(); - -#ifndef CLIENT_DLL DECLARE_ACTTABLE(); -#endif #ifdef CLIENT_DLL virtual int DrawModel( int flags );