aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/shared/hl2mp
diff options
context:
space:
mode:
authorNarendra Umate <[email protected]>2013-12-02 23:36:05 -0800
committerNarendra Umate <[email protected]>2013-12-02 23:36:05 -0800
commit8737f191f3b59f001a77bf6c08091109211c1c9f (patch)
treedbbf05c004d9b026f2c1f23f06600fe0add82c36 /mp/src/game/shared/hl2mp
parentUpdate .gitignore. (diff)
parentMake .xcconfigs text files too. (diff)
downloadsource-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.tar.xz
source-sdk-2013-8737f191f3b59f001a77bf6c08091109211c1c9f.zip
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'mp/src/game/shared/hl2mp')
-rw-r--r--mp/src/game/shared/hl2mp/hl2mp_gamerules.cpp2558
-rw-r--r--mp/src/game/shared/hl2mp/hl2mp_gamerules.h344
-rw-r--r--mp/src/game/shared/hl2mp/hl2mp_player_shared.cpp1152
-rw-r--r--mp/src/game/shared/hl2mp/hl2mp_player_shared.h194
-rw-r--r--mp/src/game/shared/hl2mp/hl2mp_weapon_parse.cpp64
-rw-r--r--mp/src/game/shared/hl2mp/hl2mp_weapon_parse.h70
-rw-r--r--mp/src/game/shared/hl2mp/weapon_357.cpp308
-rw-r--r--mp/src/game/shared/hl2mp/weapon_ar2.cpp624
-rw-r--r--mp/src/game/shared/hl2mp/weapon_ar2.h168
-rw-r--r--mp/src/game/shared/hl2mp/weapon_crossbow.cpp1912
-rw-r--r--mp/src/game/shared/hl2mp/weapon_crowbar.cpp456
-rw-r--r--mp/src/game/shared/hl2mp/weapon_crowbar.h140
-rw-r--r--mp/src/game/shared/hl2mp/weapon_frag.cpp1104
-rw-r--r--mp/src/game/shared/hl2mp/weapon_hl2mpbase.cpp660
-rw-r--r--mp/src/game/shared/hl2mp/weapon_hl2mpbase.h186
-rw-r--r--mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp484
-rw-r--r--mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h116
-rw-r--r--mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp726
-rw-r--r--mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h130
-rw-r--r--mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp830
-rw-r--r--mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h126
-rw-r--r--mp/src/game/shared/hl2mp/weapon_physcannon.cpp7370
-rw-r--r--mp/src/game/shared/hl2mp/weapon_physcannon.h60
-rw-r--r--mp/src/game/shared/hl2mp/weapon_pistol.cpp678
-rw-r--r--mp/src/game/shared/hl2mp/weapon_rpg.cpp4566
-rw-r--r--mp/src/game/shared/hl2mp/weapon_rpg.h534
-rw-r--r--mp/src/game/shared/hl2mp/weapon_shotgun.cpp1268
-rw-r--r--mp/src/game/shared/hl2mp/weapon_slam.cpp2104
-rw-r--r--mp/src/game/shared/hl2mp/weapon_slam.h182
-rw-r--r--mp/src/game/shared/hl2mp/weapon_smg1.cpp528
-rw-r--r--mp/src/game/shared/hl2mp/weapon_stunstick.cpp1802
31 files changed, 15722 insertions, 15722 deletions
diff --git a/mp/src/game/shared/hl2mp/hl2mp_gamerules.cpp b/mp/src/game/shared/hl2mp/hl2mp_gamerules.cpp
index 1b7cf70f..604e409c 100644
--- a/mp/src/game/shared/hl2mp/hl2mp_gamerules.cpp
+++ b/mp/src/game/shared/hl2mp/hl2mp_gamerules.cpp
@@ -1,1280 +1,1280 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-#include "cbase.h"
-#include "hl2mp_gamerules.h"
-#include "viewport_panel_names.h"
-#include "gameeventdefs.h"
-#include <KeyValues.h>
-#include "ammodef.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
-#else
-
- #include "eventqueue.h"
- #include "player.h"
- #include "gamerules.h"
- #include "game.h"
- #include "items.h"
- #include "entitylist.h"
- #include "mapentities.h"
- #include "in_buttons.h"
- #include <ctype.h>
- #include "voice_gamemgr.h"
- #include "iscorer.h"
- #include "hl2mp_player.h"
- #include "weapon_hl2mpbasehlmpcombatweapon.h"
- #include "team.h"
- #include "voice_gamemgr.h"
- #include "hl2mp_gameinterface.h"
- #include "hl2mp_cvars.h"
-
-#ifdef DEBUG
- #include "hl2mp_bot_temp.h"
-#endif
-
-extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse);
-
-extern bool FindInList( const char **pStrings, const char *pToFind );
-
-ConVar sv_hl2mp_weapon_respawn_time( "sv_hl2mp_weapon_respawn_time", "20", FCVAR_GAMEDLL | FCVAR_NOTIFY );
-ConVar sv_hl2mp_item_respawn_time( "sv_hl2mp_item_respawn_time", "30", FCVAR_GAMEDLL | FCVAR_NOTIFY );
-ConVar sv_report_client_settings("sv_report_client_settings", "0", FCVAR_GAMEDLL | FCVAR_NOTIFY );
-
-extern ConVar mp_chattime;
-
-extern CBaseEntity *g_pLastCombineSpawn;
-extern CBaseEntity *g_pLastRebelSpawn;
-
-#define WEAPON_MAX_DISTANCE_FROM_SPAWN 64
-
-#endif
-
-
-REGISTER_GAMERULES_CLASS( CHL2MPRules );
-
-BEGIN_NETWORK_TABLE_NOBASE( CHL2MPRules, DT_HL2MPRules )
-
- #ifdef CLIENT_DLL
- RecvPropBool( RECVINFO( m_bTeamPlayEnabled ) ),
- #else
- SendPropBool( SENDINFO( m_bTeamPlayEnabled ) ),
- #endif
-
-END_NETWORK_TABLE()
-
-
-LINK_ENTITY_TO_CLASS( hl2mp_gamerules, CHL2MPGameRulesProxy );
-IMPLEMENT_NETWORKCLASS_ALIASED( HL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
-
-static HL2MPViewVectors g_HL2MPViewVectors(
- Vector( 0, 0, 64 ), //VEC_VIEW (m_vView)
-
- Vector(-16, -16, 0 ), //VEC_HULL_MIN (m_vHullMin)
- Vector( 16, 16, 72 ), //VEC_HULL_MAX (m_vHullMax)
-
- Vector(-16, -16, 0 ), //VEC_DUCK_HULL_MIN (m_vDuckHullMin)
- Vector( 16, 16, 36 ), //VEC_DUCK_HULL_MAX (m_vDuckHullMax)
- Vector( 0, 0, 28 ), //VEC_DUCK_VIEW (m_vDuckView)
-
- Vector(-10, -10, -10 ), //VEC_OBS_HULL_MIN (m_vObsHullMin)
- Vector( 10, 10, 10 ), //VEC_OBS_HULL_MAX (m_vObsHullMax)
-
- Vector( 0, 0, 14 ), //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight)
-
- Vector(-16, -16, 0 ), //VEC_CROUCH_TRACE_MIN (m_vCrouchTraceMin)
- Vector( 16, 16, 60 ) //VEC_CROUCH_TRACE_MAX (m_vCrouchTraceMax)
-);
-
-static const char *s_PreserveEnts[] =
-{
- "ai_network",
- "ai_hint",
- "hl2mp_gamerules",
- "team_manager",
- "player_manager",
- "env_soundscape",
- "env_soundscape_proxy",
- "env_soundscape_triggerable",
- "env_sun",
- "env_wind",
- "env_fog_controller",
- "func_brush",
- "func_wall",
- "func_buyzone",
- "func_illusionary",
- "infodecal",
- "info_projecteddecal",
- "info_node",
- "info_target",
- "info_node_hint",
- "info_player_deathmatch",
- "info_player_combine",
- "info_player_rebel",
- "info_map_parameters",
- "keyframe_rope",
- "move_rope",
- "info_ladder",
- "player",
- "point_viewcontrol",
- "scene_manager",
- "shadow_control",
- "sky_camera",
- "soundent",
- "trigger_soundscape",
- "viewmodel",
- "predicted_viewmodel",
- "worldspawn",
- "point_devshot_camera",
- "", // END Marker
-};
-
-
-
-#ifdef CLIENT_DLL
- void RecvProxy_HL2MPRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
- {
- CHL2MPRules *pRules = HL2MPRules();
- Assert( pRules );
- *pOut = pRules;
- }
-
- BEGIN_RECV_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
- RecvPropDataTable( "hl2mp_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2MPRules ), RecvProxy_HL2MPRules )
- END_RECV_TABLE()
-#else
- void* SendProxy_HL2MPRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
- {
- CHL2MPRules *pRules = HL2MPRules();
- Assert( pRules );
- return pRules;
- }
-
- BEGIN_SEND_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
- SendPropDataTable( "hl2mp_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_HL2MPRules ), SendProxy_HL2MPRules )
- END_SEND_TABLE()
-#endif
-
-#ifndef CLIENT_DLL
-
- class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
- {
- public:
- virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity )
- {
- return ( pListener->GetTeamNumber() == pTalker->GetTeamNumber() );
- }
- };
- CVoiceGameMgrHelper g_VoiceGameMgrHelper;
- IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
-
-#endif
-
-// NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
-char *sTeamNames[] =
-{
- "Unassigned",
- "Spectator",
- "Combine",
- "Rebels",
-};
-
-CHL2MPRules::CHL2MPRules()
-{
-#ifndef CLIENT_DLL
- // Create the team managers
- for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ )
- {
- CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "team_manager" ));
- pTeam->Init( sTeamNames[i], i );
-
- g_Teams.AddToTail( pTeam );
- }
-
- m_bTeamPlayEnabled = teamplay.GetBool();
- m_flIntermissionEndTime = 0.0f;
- m_flGameStartTime = 0;
-
- m_hRespawnableItemsAndWeapons.RemoveAll();
- m_tmNextPeriodicThink = 0;
- m_flRestartGameTime = 0;
- m_bCompleteReset = false;
- m_bHeardAllPlayersReady = false;
- m_bAwaitingReadyRestart = false;
- m_bChangelevelDone = false;
-
-#endif
-}
-
-const CViewVectors* CHL2MPRules::GetViewVectors()const
-{
- return &g_HL2MPViewVectors;
-}
-
-const HL2MPViewVectors* CHL2MPRules::GetHL2MPViewVectors()const
-{
- return &g_HL2MPViewVectors;
-}
-
-CHL2MPRules::~CHL2MPRules( void )
-{
-#ifndef CLIENT_DLL
- // Note, don't delete each team since they are in the gEntList and will
- // automatically be deleted from there, instead.
- g_Teams.Purge();
-#endif
-}
-
-void CHL2MPRules::CreateStandardEntities( void )
-{
-
-#ifndef CLIENT_DLL
- // Create the entity that will send our data to the client.
-
- BaseClass::CreateStandardEntities();
-
- g_pLastCombineSpawn = NULL;
- g_pLastRebelSpawn = NULL;
-
-#ifdef DBGFLAG_ASSERT
- CBaseEntity *pEnt =
-#endif
- CBaseEntity::Create( "hl2mp_gamerules", vec3_origin, vec3_angle );
- Assert( pEnt );
-#endif
-}
-
-//=========================================================
-// FlWeaponRespawnTime - what is the time in the future
-// at which this weapon may spawn?
-//=========================================================
-float CHL2MPRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon )
-{
-#ifndef CLIENT_DLL
- if ( weaponstay.GetInt() > 0 )
- {
- // make sure it's only certain weapons
- if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
- {
- return 0; // weapon respawns almost instantly
- }
- }
-
- return sv_hl2mp_weapon_respawn_time.GetFloat();
-#endif
-
- return 0; // weapon respawns almost instantly
-}
-
-
-bool CHL2MPRules::IsIntermission( void )
-{
-#ifndef CLIENT_DLL
- return m_flIntermissionEndTime > gpGlobals->curtime;
-#endif
-
- return false;
-}
-
-void CHL2MPRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
-{
-#ifndef CLIENT_DLL
- if ( IsIntermission() )
- return;
- BaseClass::PlayerKilled( pVictim, info );
-#endif
-}
-
-
-void CHL2MPRules::Think( void )
-{
-
-#ifndef CLIENT_DLL
-
- CGameRules::Think();
-
- if ( g_fGameOver ) // someone else quit the game already
- {
- // check to see if we should change levels now
- if ( m_flIntermissionEndTime < gpGlobals->curtime )
- {
- if ( !m_bChangelevelDone )
- {
- ChangeLevel(); // intermission is over
- m_bChangelevelDone = true;
- }
- }
-
- return;
- }
-
-// float flTimeLimit = mp_timelimit.GetFloat() * 60;
- float flFragLimit = fraglimit.GetFloat();
-
- if ( GetMapRemainingTime() < 0 )
- {
- GoToIntermission();
- return;
- }
-
- if ( flFragLimit )
- {
- if( IsTeamplay() == true )
- {
- CTeam *pCombine = g_Teams[TEAM_COMBINE];
- CTeam *pRebels = g_Teams[TEAM_REBELS];
-
- if ( pCombine->GetScore() >= flFragLimit || pRebels->GetScore() >= flFragLimit )
- {
- GoToIntermission();
- return;
- }
- }
- else
- {
- // check if any player is over the frag limit
- for ( int i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
-
- if ( pPlayer && pPlayer->FragCount() >= flFragLimit )
- {
- GoToIntermission();
- return;
- }
- }
- }
- }
-
- if ( gpGlobals->curtime > m_tmNextPeriodicThink )
- {
- CheckAllPlayersReady();
- CheckRestartGame();
- m_tmNextPeriodicThink = gpGlobals->curtime + 1.0;
- }
-
- if ( m_flRestartGameTime > 0.0f && m_flRestartGameTime <= gpGlobals->curtime )
- {
- RestartGame();
- }
-
- if( m_bAwaitingReadyRestart && m_bHeardAllPlayersReady )
- {
- UTIL_ClientPrintAll( HUD_PRINTCENTER, "All players ready. Game will restart in 5 seconds" );
- UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "All players ready. Game will restart in 5 seconds" );
-
- m_flRestartGameTime = gpGlobals->curtime + 5;
- m_bAwaitingReadyRestart = false;
- }
-
- ManageObjectRelocation();
-
-#endif
-}
-
-void CHL2MPRules::GoToIntermission( void )
-{
-#ifndef CLIENT_DLL
- if ( g_fGameOver )
- return;
-
- g_fGameOver = true;
-
- m_flIntermissionEndTime = gpGlobals->curtime + mp_chattime.GetInt();
-
- for ( int i = 0; i < MAX_PLAYERS; i++ )
- {
- CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
-
- if ( !pPlayer )
- continue;
-
- pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD );
- pPlayer->AddFlag( FL_FROZEN );
- }
-#endif
-
-}
-
-bool CHL2MPRules::CheckGameOver()
-{
-#ifndef CLIENT_DLL
- if ( g_fGameOver ) // someone else quit the game already
- {
- // check to see if we should change levels now
- if ( m_flIntermissionEndTime < gpGlobals->curtime )
- {
- ChangeLevel(); // intermission is over
- }
-
- return true;
- }
-#endif
-
- return false;
-}
-
-// when we are within this close to running out of entities, items
-// marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn
-#define ENTITY_INTOLERANCE 100
-
-//=========================================================
-// FlWeaponRespawnTime - Returns 0 if the weapon can respawn
-// now, otherwise it returns the time at which it can try
-// to spawn again.
-//=========================================================
-float CHL2MPRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon )
-{
-#ifndef CLIENT_DLL
- if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
- {
- if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) )
- return 0;
-
- // we're past the entity tolerance level, so delay the respawn
- return FlWeaponRespawnTime( pWeapon );
- }
-#endif
- return 0;
-}
-
-//=========================================================
-// VecWeaponRespawnSpot - where should this weapon spawn?
-// Some game variations may choose to randomize spawn locations
-//=========================================================
-Vector CHL2MPRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon )
-{
-#ifndef CLIENT_DLL
- CWeaponHL2MPBase *pHL2Weapon = dynamic_cast< CWeaponHL2MPBase*>( pWeapon );
-
- if ( pHL2Weapon )
- {
- return pHL2Weapon->GetOriginalSpawnOrigin();
- }
-#endif
-
- return pWeapon->GetAbsOrigin();
-}
-
-#ifndef CLIENT_DLL
-
-CItem* IsManagedObjectAnItem( CBaseEntity *pObject )
-{
- return dynamic_cast< CItem*>( pObject );
-}
-
-CWeaponHL2MPBase* IsManagedObjectAWeapon( CBaseEntity *pObject )
-{
- return dynamic_cast< CWeaponHL2MPBase*>( pObject );
-}
-
-bool GetObjectsOriginalParameters( CBaseEntity *pObject, Vector &vOriginalOrigin, QAngle &vOriginalAngles )
-{
- if ( CItem *pItem = IsManagedObjectAnItem( pObject ) )
- {
- if ( pItem->m_flNextResetCheckTime > gpGlobals->curtime )
- return false;
-
- vOriginalOrigin = pItem->GetOriginalSpawnOrigin();
- vOriginalAngles = pItem->GetOriginalSpawnAngles();
-
- pItem->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_item_respawn_time.GetFloat();
- return true;
- }
- else if ( CWeaponHL2MPBase *pWeapon = IsManagedObjectAWeapon( pObject ))
- {
- if ( pWeapon->m_flNextResetCheckTime > gpGlobals->curtime )
- return false;
-
- vOriginalOrigin = pWeapon->GetOriginalSpawnOrigin();
- vOriginalAngles = pWeapon->GetOriginalSpawnAngles();
-
- pWeapon->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_weapon_respawn_time.GetFloat();
- return true;
- }
-
- return false;
-}
-
-void CHL2MPRules::ManageObjectRelocation( void )
-{
- int iTotal = m_hRespawnableItemsAndWeapons.Count();
-
- if ( iTotal > 0 )
- {
- for ( int i = 0; i < iTotal; i++ )
- {
- CBaseEntity *pObject = m_hRespawnableItemsAndWeapons[i].Get();
-
- if ( pObject )
- {
- Vector vSpawOrigin;
- QAngle vSpawnAngles;
-
- if ( GetObjectsOriginalParameters( pObject, vSpawOrigin, vSpawnAngles ) == true )
- {
- float flDistanceFromSpawn = (pObject->GetAbsOrigin() - vSpawOrigin ).Length();
-
- if ( flDistanceFromSpawn > WEAPON_MAX_DISTANCE_FROM_SPAWN )
- {
- bool shouldReset = false;
- IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
-
- if ( pPhysics )
- {
- shouldReset = pPhysics->IsAsleep();
- }
- else
- {
- shouldReset = (pObject->GetFlags() & FL_ONGROUND) ? true : false;
- }
-
- if ( shouldReset )
- {
- pObject->Teleport( &vSpawOrigin, &vSpawnAngles, NULL );
- pObject->EmitSound( "AlyxEmp.Charge" );
-
- IPhysicsObject *pPhys = pObject->VPhysicsGetObject();
-
- if ( pPhys )
- {
- pPhys->Wake();
- }
- }
- }
- }
- }
- }
- }
-}
-
-//=========================================================
-//AddLevelDesignerPlacedWeapon
-//=========================================================
-void CHL2MPRules::AddLevelDesignerPlacedObject( CBaseEntity *pEntity )
-{
- if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) == -1 )
- {
- m_hRespawnableItemsAndWeapons.AddToTail( pEntity );
- }
-}
-
-//=========================================================
-//RemoveLevelDesignerPlacedWeapon
-//=========================================================
-void CHL2MPRules::RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity )
-{
- if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) != -1 )
- {
- m_hRespawnableItemsAndWeapons.FindAndRemove( pEntity );
- }
-}
-
-//=========================================================
-// Where should this item respawn?
-// Some game variations may choose to randomize spawn locations
-//=========================================================
-Vector CHL2MPRules::VecItemRespawnSpot( CItem *pItem )
-{
- return pItem->GetOriginalSpawnOrigin();
-}
-
-//=========================================================
-// What angles should this item use to respawn?
-//=========================================================
-QAngle CHL2MPRules::VecItemRespawnAngles( CItem *pItem )
-{
- return pItem->GetOriginalSpawnAngles();
-}
-
-//=========================================================
-// At what time in the future may this Item respawn?
-//=========================================================
-float CHL2MPRules::FlItemRespawnTime( CItem *pItem )
-{
- return sv_hl2mp_item_respawn_time.GetFloat();
-}
-
-
-//=========================================================
-// CanHaveWeapon - returns false if the player is not allowed
-// to pick up this weapon
-//=========================================================
-bool CHL2MPRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem )
-{
- if ( weaponstay.GetInt() > 0 )
- {
- if ( pPlayer->Weapon_OwnsThisType( pItem->GetClassname(), pItem->GetSubType() ) )
- return false;
- }
-
- return BaseClass::CanHavePlayerItem( pPlayer, pItem );
-}
-
-#endif
-
-//=========================================================
-// WeaponShouldRespawn - any conditions inhibiting the
-// respawning of this weapon?
-//=========================================================
-int CHL2MPRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon )
-{
-#ifndef CLIENT_DLL
- if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) )
- {
- return GR_WEAPON_RESPAWN_NO;
- }
-#endif
-
- return GR_WEAPON_RESPAWN_YES;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Player has just left the game
-//-----------------------------------------------------------------------------
-void CHL2MPRules::ClientDisconnected( edict_t *pClient )
-{
-#ifndef CLIENT_DLL
- // Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" );
-
- CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
- if ( pPlayer )
- {
- // Remove the player from his team
- if ( pPlayer->GetTeam() )
- {
- pPlayer->GetTeam()->RemovePlayer( pPlayer );
- }
- }
-
- BaseClass::ClientDisconnected( pClient );
-
-#endif
-}
-
-
-//=========================================================
-// Deathnotice.
-//=========================================================
-void CHL2MPRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
-{
-#ifndef CLIENT_DLL
- // Work out what killed the player, and send a message to all clients about it
- const char *killer_weapon_name = "world"; // by default, the player is killed by the world
- int killer_ID = 0;
-
- // Find the killer & the scorer
- CBaseEntity *pInflictor = info.GetInflictor();
- CBaseEntity *pKiller = info.GetAttacker();
- CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
-
- // Custom kill type?
- if ( info.GetDamageCustom() )
- {
- killer_weapon_name = GetDamageCustomString( info );
- if ( pScorer )
- {
- killer_ID = pScorer->GetUserID();
- }
- }
- else
- {
- // Is the killer a client?
- if ( pScorer )
- {
- killer_ID = pScorer->GetUserID();
-
- if ( pInflictor )
- {
- if ( pInflictor == pScorer )
- {
- // If the inflictor is the killer, then it must be their current weapon doing the damage
- if ( pScorer->GetActiveWeapon() )
- {
- killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname();
- }
- }
- else
- {
- killer_weapon_name = pInflictor->GetClassname(); // it's just that easy
- }
- }
- }
- else
- {
- killer_weapon_name = pInflictor->GetClassname();
- }
-
- // strip the NPC_* or weapon_* from the inflictor's classname
- if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 )
- {
- killer_weapon_name += 7;
- }
- else if ( strncmp( killer_weapon_name, "npc_", 4 ) == 0 )
- {
- killer_weapon_name += 4;
- }
- else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 )
- {
- killer_weapon_name += 5;
- }
- else if ( strstr( killer_weapon_name, "physics" ) )
- {
- killer_weapon_name = "physics";
- }
-
- if ( strcmp( killer_weapon_name, "prop_combine_ball" ) == 0 )
- {
- killer_weapon_name = "combine_ball";
- }
- else if ( strcmp( killer_weapon_name, "grenade_ar2" ) == 0 )
- {
- killer_weapon_name = "smg1_grenade";
- }
- else if ( strcmp( killer_weapon_name, "satchel" ) == 0 || strcmp( killer_weapon_name, "tripmine" ) == 0)
- {
- killer_weapon_name = "slam";
- }
-
-
- }
-
- IGameEvent *event = gameeventmanager->CreateEvent( "player_death" );
- if( event )
- {
- event->SetInt("userid", pVictim->GetUserID() );
- event->SetInt("attacker", killer_ID );
- event->SetString("weapon", killer_weapon_name );
- event->SetInt( "priority", 7 );
- gameeventmanager->FireEvent( event );
- }
-#endif
-
-}
-
-void CHL2MPRules::ClientSettingsChanged( CBasePlayer *pPlayer )
-{
-#ifndef CLIENT_DLL
-
- CHL2MP_Player *pHL2Player = ToHL2MPPlayer( pPlayer );
-
- if ( pHL2Player == NULL )
- return;
-
- const char *pCurrentModel = modelinfo->GetModelName( pPlayer->GetModel() );
- const char *szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_playermodel" );
-
- //If we're different.
- if ( stricmp( szModelName, pCurrentModel ) )
- {
- //Too soon, set the cvar back to what it was.
- //Note: this will make this function be called again
- //but since our models will match it'll just skip this whole dealio.
- if ( pHL2Player->GetNextModelChangeTime() >= gpGlobals->curtime )
- {
- char szReturnString[512];
-
- Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pCurrentModel );
- engine->ClientCommand ( pHL2Player->edict(), szReturnString );
-
- Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch.\n", (int)(pHL2Player->GetNextModelChangeTime() - gpGlobals->curtime) );
- ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString );
- return;
- }
-
- if ( HL2MPRules()->IsTeamplay() == false )
- {
- pHL2Player->SetPlayerModel();
-
- const char *pszCurrentModelName = modelinfo->GetModelName( pHL2Player->GetModel() );
-
- char szReturnString[128];
- Q_snprintf( szReturnString, sizeof( szReturnString ), "Your player model is: %s\n", pszCurrentModelName );
-
- ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString );
- }
- else
- {
- if ( Q_stristr( szModelName, "models/human") )
- {
- pHL2Player->ChangeTeam( TEAM_REBELS );
- }
- else
- {
- pHL2Player->ChangeTeam( TEAM_COMBINE );
- }
- }
- }
- if ( sv_report_client_settings.GetInt() == 1 )
- {
- UTIL_LogPrintf( "\"%s\" cl_cmdrate = \"%s\"\n", pHL2Player->GetPlayerName(), engine->GetClientConVarValue( pHL2Player->entindex(), "cl_cmdrate" ));
- }
-
- BaseClass::ClientSettingsChanged( pPlayer );
-#endif
-
-}
-
-int CHL2MPRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
-{
-#ifndef CLIENT_DLL
- // half life multiplay has a simple concept of Player Relationships.
- // you are either on another player's team, or you are not.
- if ( !pPlayer || !pTarget || !pTarget->IsPlayer() || IsTeamplay() == false )
- return GR_NOTTEAMMATE;
-
- if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
- {
- return GR_TEAMMATE;
- }
-#endif
-
- return GR_NOTTEAMMATE;
-}
-
-const char *CHL2MPRules::GetGameDescription( void )
-{
- if ( IsTeamplay() )
- return "Team Deathmatch";
-
- return "Deathmatch";
-}
-
-bool CHL2MPRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer )
-{
- return true;
-}
-
-float CHL2MPRules::GetMapRemainingTime()
-{
- // if timelimit is disabled, return 0
- if ( mp_timelimit.GetInt() <= 0 )
- return 0;
-
- // timelimit is in minutes
-
- float timeleft = (m_flGameStartTime + mp_timelimit.GetInt() * 60.0f ) - gpGlobals->curtime;
-
- return timeleft;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CHL2MPRules::Precache( void )
-{
- CBaseEntity::PrecacheScriptSound( "AlyxEmp.Charge" );
-}
-
-bool CHL2MPRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
-{
- if ( collisionGroup0 > collisionGroup1 )
- {
- // swap so that lowest is always first
- V_swap(collisionGroup0,collisionGroup1);
- }
-
- if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
- collisionGroup1 == COLLISION_GROUP_WEAPON )
- {
- return false;
- }
-
- return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
-
-}
-
-bool CHL2MPRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
-{
-#ifndef CLIENT_DLL
- if( BaseClass::ClientCommand( pEdict, args ) )
- return true;
-
-
- CHL2MP_Player *pPlayer = (CHL2MP_Player *) pEdict;
-
- if ( pPlayer->ClientCommand( args ) )
- return true;
-#endif
-
- return false;
-}
-
-// shared ammo definition
-// JAY: Trying to make a more physical bullet response
-#define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f)
-#define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
-
-// exaggerate all of the forces, but use real numbers to keep them consistent
-#define BULLET_IMPULSE_EXAGGERATION 3.5
-// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
-#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
-
-
-CAmmoDef *GetAmmoDef()
-{
- static CAmmoDef def;
- static bool bInitted = false;
-
- if ( !bInitted )
- {
- bInitted = true;
-
- def.AddAmmoType("AR2", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 60, BULLET_IMPULSE(200, 1225), 0 );
- def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, 3, 0, 0 );
- def.AddAmmoType("Pistol", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 150, BULLET_IMPULSE(200, 1225), 0 );
- def.AddAmmoType("SMG1", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 225, BULLET_IMPULSE(200, 1225), 0 );
- def.AddAmmoType("357", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 12, BULLET_IMPULSE(800, 5000), 0 );
- def.AddAmmoType("XBowBolt", DMG_BULLET, TRACER_LINE, 0, 0, 10, BULLET_IMPULSE(800, 8000), 0 );
- def.AddAmmoType("Buckshot", DMG_BULLET | DMG_BUCKSHOT, TRACER_LINE, 0, 0, 30, BULLET_IMPULSE(400, 1200), 0 );
- def.AddAmmoType("RPG_Round", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 );
- def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 );
- def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 );
- def.AddAmmoType("slam", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 );
- }
-
- return &def;
-}
-
-#ifdef CLIENT_DLL
-
- ConVar cl_autowepswitch(
- "cl_autowepswitch",
- "1",
- FCVAR_ARCHIVE | FCVAR_USERINFO,
- "Automatically switch to picked up weapons (if more powerful)" );
-
-#else
-
-#ifdef DEBUG
-
- // Handler for the "bot" command.
- void Bot_f()
- {
- // Look at -count.
- int count = 1;
- count = clamp( count, 1, 16 );
-
- int iTeam = TEAM_COMBINE;
-
- // Look at -frozen.
- bool bFrozen = false;
-
- // Ok, spawn all the bots.
- while ( --count >= 0 )
- {
- BotPutInServer( bFrozen, iTeam );
- }
- }
-
-
- ConCommand cc_Bot( "bot", Bot_f, "Add a bot.", FCVAR_CHEAT );
-
-#endif
-
- bool CHL2MPRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
- {
- if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() )
- {
- // Player has an active item, so let's check cl_autowepswitch.
- const char *cl_autowepswitch = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_autowepswitch" );
- if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 )
- {
- return false;
- }
- }
-
- return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon );
- }
-
-#endif
-
-#ifndef CLIENT_DLL
-
-void CHL2MPRules::RestartGame()
-{
- // bounds check
- if ( mp_timelimit.GetInt() < 0 )
- {
- mp_timelimit.SetValue( 0 );
- }
- m_flGameStartTime = gpGlobals->curtime;
- if ( !IsFinite( m_flGameStartTime.Get() ) )
- {
- Warning( "Trying to set a NaN game start time\n" );
- m_flGameStartTime.GetForModify() = 0.0f;
- }
-
- CleanUpMap();
-
- // now respawn all players
- for (int i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i );
-
- if ( !pPlayer )
- continue;
-
- if ( pPlayer->GetActiveWeapon() )
- {
- pPlayer->GetActiveWeapon()->Holster();
- }
- pPlayer->RemoveAllItems( true );
- respawn( pPlayer, false );
- pPlayer->Reset();
- }
-
- // Respawn entities (glass, doors, etc..)
-
- CTeam *pRebels = GetGlobalTeam( TEAM_REBELS );
- CTeam *pCombine = GetGlobalTeam( TEAM_COMBINE );
-
- if ( pRebels )
- {
- pRebels->SetScore( 0 );
- }
-
- if ( pCombine )
- {
- pCombine->SetScore( 0 );
- }
-
- m_flIntermissionEndTime = 0;
- m_flRestartGameTime = 0.0;
- m_bCompleteReset = false;
-
- IGameEvent * event = gameeventmanager->CreateEvent( "round_start" );
- if ( event )
- {
- event->SetInt("fraglimit", 0 );
- event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
-
- event->SetString("objective","DEATHMATCH");
-
- gameeventmanager->FireEvent( event );
- }
-}
-
-void CHL2MPRules::CleanUpMap()
-{
- // Recreate all the map entities from the map data (preserving their indices),
- // then remove everything else except the players.
-
- // Get rid of all entities except players.
- CBaseEntity *pCur = gEntList.FirstEnt();
- while ( pCur )
- {
- CBaseHL2MPCombatWeapon *pWeapon = dynamic_cast< CBaseHL2MPCombatWeapon* >( pCur );
- // Weapons with owners don't want to be removed..
- if ( pWeapon )
- {
- if ( !pWeapon->GetPlayerOwner() )
- {
- UTIL_Remove( pCur );
- }
- }
- // remove entities that has to be restored on roundrestart (breakables etc)
- else if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) )
- {
- UTIL_Remove( pCur );
- }
-
- pCur = gEntList.NextEnt( pCur );
- }
-
- // Really remove the entities so we can have access to their slots below.
- gEntList.CleanupDeleteList();
-
- // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that
- // could kill respawning CTs
- g_EventQueue.Clear();
-
- // Now reload the map entities.
- class CHL2MPMapEntityFilter : public IMapEntityFilter
- {
- public:
- virtual bool ShouldCreateEntity( const char *pClassname )
- {
- // Don't recreate the preserved entities.
- if ( !FindInList( s_PreserveEnts, pClassname ) )
- {
- return true;
- }
- else
- {
- // Increment our iterator since it's not going to call CreateNextEntity for this ent.
- if ( m_iIterator != g_MapEntityRefs.InvalidIndex() )
- m_iIterator = g_MapEntityRefs.Next( m_iIterator );
-
- return false;
- }
- }
-
-
- virtual CBaseEntity* CreateNextEntity( const char *pClassname )
- {
- if ( m_iIterator == g_MapEntityRefs.InvalidIndex() )
- {
- // This shouldn't be possible. When we loaded the map, it should have used
- // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list
- // with the same list of entities we're referring to here.
- Assert( false );
- return NULL;
- }
- else
- {
- CMapEntityRef &ref = g_MapEntityRefs[m_iIterator];
- m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity.
-
- if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) )
- {
- // Doh! The entity was delete and its slot was reused.
- // Just use any old edict slot. This case sucks because we lose the baseline.
- return CreateEntityByName( pClassname );
- }
- else
- {
- // Cool, the slot where this entity was is free again (most likely, the entity was
- // freed above). Now create an entity with this specific index.
- return CreateEntityByName( pClassname, ref.m_iEdict );
- }
- }
- }
-
- public:
- int m_iIterator; // Iterator into g_MapEntityRefs.
- };
- CHL2MPMapEntityFilter filter;
- filter.m_iIterator = g_MapEntityRefs.Head();
-
- // DO NOT CALL SPAWN ON info_node ENTITIES!
-
- MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
-}
-
-void CHL2MPRules::CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg )
-{
- if( m_bAwaitingReadyRestart && FStrEq( chatmsg, mp_ready_signal.GetString() ) )
- {
- if( !pPlayer->IsReady() )
- {
- pPlayer->SetReady( true );
- }
- }
-}
-
-void CHL2MPRules::CheckRestartGame( void )
-{
- // Restart the game if specified by the server
- int iRestartDelay = mp_restartgame.GetInt();
-
- if ( iRestartDelay > 0 )
- {
- if ( iRestartDelay > 60 )
- iRestartDelay = 60;
-
-
- // let the players know
- char strRestartDelay[64];
- Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay );
- UTIL_ClientPrintAll( HUD_PRINTCENTER, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
- UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
-
- m_flRestartGameTime = gpGlobals->curtime + iRestartDelay;
- m_bCompleteReset = true;
- mp_restartgame.SetValue( 0 );
- }
-
- if( mp_readyrestart.GetBool() )
- {
- m_bAwaitingReadyRestart = true;
- m_bHeardAllPlayersReady = false;
-
-
- const char *pszReadyString = mp_ready_signal.GetString();
-
-
- // Don't let them put anything malicious in there
- if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 )
- {
- pszReadyString = "ready";
- }
-
- IGameEvent *event = gameeventmanager->CreateEvent( "hl2mp_ready_restart" );
- if ( event )
- gameeventmanager->FireEvent( event );
-
- mp_readyrestart.SetValue( 0 );
-
- // cancel any restart round in progress
- m_flRestartGameTime = -1;
- }
-}
-
-void CHL2MPRules::CheckAllPlayersReady( void )
-{
- for (int i = 1; i <= gpGlobals->maxClients; i++ )
- {
- CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i );
-
- if ( !pPlayer )
- continue;
- if ( !pPlayer->IsReady() )
- return;
- }
- m_bHeardAllPlayersReady = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-const char *CHL2MPRules::GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer )
-{
- if ( !pPlayer ) // dedicated server output
- {
- return NULL;
- }
-
- const char *pszFormat = NULL;
-
- // team only
- if ( bTeamOnly == TRUE )
- {
- if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
- {
- pszFormat = "HL2MP_Chat_Spec";
- }
- else
- {
- const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
- if ( chatLocation && *chatLocation )
- {
- pszFormat = "HL2MP_Chat_Team_Loc";
- }
- else
- {
- pszFormat = "HL2MP_Chat_Team";
- }
- }
- }
- // everyone
- else
- {
- if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
- {
- pszFormat = "HL2MP_Chat_All";
- }
- else
- {
- pszFormat = "HL2MP_Chat_AllSpec";
- }
- }
-
- return pszFormat;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "hl2mp_gamerules.h"
+#include "viewport_panel_names.h"
+#include "gameeventdefs.h"
+#include <KeyValues.h>
+#include "ammodef.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+#else
+
+ #include "eventqueue.h"
+ #include "player.h"
+ #include "gamerules.h"
+ #include "game.h"
+ #include "items.h"
+ #include "entitylist.h"
+ #include "mapentities.h"
+ #include "in_buttons.h"
+ #include <ctype.h>
+ #include "voice_gamemgr.h"
+ #include "iscorer.h"
+ #include "hl2mp_player.h"
+ #include "weapon_hl2mpbasehlmpcombatweapon.h"
+ #include "team.h"
+ #include "voice_gamemgr.h"
+ #include "hl2mp_gameinterface.h"
+ #include "hl2mp_cvars.h"
+
+#ifdef DEBUG
+ #include "hl2mp_bot_temp.h"
+#endif
+
+extern void respawn(CBaseEntity *pEdict, bool fCopyCorpse);
+
+extern bool FindInList( const char **pStrings, const char *pToFind );
+
+ConVar sv_hl2mp_weapon_respawn_time( "sv_hl2mp_weapon_respawn_time", "20", FCVAR_GAMEDLL | FCVAR_NOTIFY );
+ConVar sv_hl2mp_item_respawn_time( "sv_hl2mp_item_respawn_time", "30", FCVAR_GAMEDLL | FCVAR_NOTIFY );
+ConVar sv_report_client_settings("sv_report_client_settings", "0", FCVAR_GAMEDLL | FCVAR_NOTIFY );
+
+extern ConVar mp_chattime;
+
+extern CBaseEntity *g_pLastCombineSpawn;
+extern CBaseEntity *g_pLastRebelSpawn;
+
+#define WEAPON_MAX_DISTANCE_FROM_SPAWN 64
+
+#endif
+
+
+REGISTER_GAMERULES_CLASS( CHL2MPRules );
+
+BEGIN_NETWORK_TABLE_NOBASE( CHL2MPRules, DT_HL2MPRules )
+
+ #ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bTeamPlayEnabled ) ),
+ #else
+ SendPropBool( SENDINFO( m_bTeamPlayEnabled ) ),
+ #endif
+
+END_NETWORK_TABLE()
+
+
+LINK_ENTITY_TO_CLASS( hl2mp_gamerules, CHL2MPGameRulesProxy );
+IMPLEMENT_NETWORKCLASS_ALIASED( HL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
+
+static HL2MPViewVectors g_HL2MPViewVectors(
+ Vector( 0, 0, 64 ), //VEC_VIEW (m_vView)
+
+ Vector(-16, -16, 0 ), //VEC_HULL_MIN (m_vHullMin)
+ Vector( 16, 16, 72 ), //VEC_HULL_MAX (m_vHullMax)
+
+ Vector(-16, -16, 0 ), //VEC_DUCK_HULL_MIN (m_vDuckHullMin)
+ Vector( 16, 16, 36 ), //VEC_DUCK_HULL_MAX (m_vDuckHullMax)
+ Vector( 0, 0, 28 ), //VEC_DUCK_VIEW (m_vDuckView)
+
+ Vector(-10, -10, -10 ), //VEC_OBS_HULL_MIN (m_vObsHullMin)
+ Vector( 10, 10, 10 ), //VEC_OBS_HULL_MAX (m_vObsHullMax)
+
+ Vector( 0, 0, 14 ), //VEC_DEAD_VIEWHEIGHT (m_vDeadViewHeight)
+
+ Vector(-16, -16, 0 ), //VEC_CROUCH_TRACE_MIN (m_vCrouchTraceMin)
+ Vector( 16, 16, 60 ) //VEC_CROUCH_TRACE_MAX (m_vCrouchTraceMax)
+);
+
+static const char *s_PreserveEnts[] =
+{
+ "ai_network",
+ "ai_hint",
+ "hl2mp_gamerules",
+ "team_manager",
+ "player_manager",
+ "env_soundscape",
+ "env_soundscape_proxy",
+ "env_soundscape_triggerable",
+ "env_sun",
+ "env_wind",
+ "env_fog_controller",
+ "func_brush",
+ "func_wall",
+ "func_buyzone",
+ "func_illusionary",
+ "infodecal",
+ "info_projecteddecal",
+ "info_node",
+ "info_target",
+ "info_node_hint",
+ "info_player_deathmatch",
+ "info_player_combine",
+ "info_player_rebel",
+ "info_map_parameters",
+ "keyframe_rope",
+ "move_rope",
+ "info_ladder",
+ "player",
+ "point_viewcontrol",
+ "scene_manager",
+ "shadow_control",
+ "sky_camera",
+ "soundent",
+ "trigger_soundscape",
+ "viewmodel",
+ "predicted_viewmodel",
+ "worldspawn",
+ "point_devshot_camera",
+ "", // END Marker
+};
+
+
+
+#ifdef CLIENT_DLL
+ void RecvProxy_HL2MPRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
+ {
+ CHL2MPRules *pRules = HL2MPRules();
+ Assert( pRules );
+ *pOut = pRules;
+ }
+
+ BEGIN_RECV_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
+ RecvPropDataTable( "hl2mp_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2MPRules ), RecvProxy_HL2MPRules )
+ END_RECV_TABLE()
+#else
+ void* SendProxy_HL2MPRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
+ {
+ CHL2MPRules *pRules = HL2MPRules();
+ Assert( pRules );
+ return pRules;
+ }
+
+ BEGIN_SEND_TABLE( CHL2MPGameRulesProxy, DT_HL2MPGameRulesProxy )
+ SendPropDataTable( "hl2mp_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_HL2MPRules ), SendProxy_HL2MPRules )
+ END_SEND_TABLE()
+#endif
+
+#ifndef CLIENT_DLL
+
+ class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
+ {
+ public:
+ virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity )
+ {
+ return ( pListener->GetTeamNumber() == pTalker->GetTeamNumber() );
+ }
+ };
+ CVoiceGameMgrHelper g_VoiceGameMgrHelper;
+ IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
+
+#endif
+
+// NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
+char *sTeamNames[] =
+{
+ "Unassigned",
+ "Spectator",
+ "Combine",
+ "Rebels",
+};
+
+CHL2MPRules::CHL2MPRules()
+{
+#ifndef CLIENT_DLL
+ // Create the team managers
+ for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ )
+ {
+ CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "team_manager" ));
+ pTeam->Init( sTeamNames[i], i );
+
+ g_Teams.AddToTail( pTeam );
+ }
+
+ m_bTeamPlayEnabled = teamplay.GetBool();
+ m_flIntermissionEndTime = 0.0f;
+ m_flGameStartTime = 0;
+
+ m_hRespawnableItemsAndWeapons.RemoveAll();
+ m_tmNextPeriodicThink = 0;
+ m_flRestartGameTime = 0;
+ m_bCompleteReset = false;
+ m_bHeardAllPlayersReady = false;
+ m_bAwaitingReadyRestart = false;
+ m_bChangelevelDone = false;
+
+#endif
+}
+
+const CViewVectors* CHL2MPRules::GetViewVectors()const
+{
+ return &g_HL2MPViewVectors;
+}
+
+const HL2MPViewVectors* CHL2MPRules::GetHL2MPViewVectors()const
+{
+ return &g_HL2MPViewVectors;
+}
+
+CHL2MPRules::~CHL2MPRules( void )
+{
+#ifndef CLIENT_DLL
+ // Note, don't delete each team since they are in the gEntList and will
+ // automatically be deleted from there, instead.
+ g_Teams.Purge();
+#endif
+}
+
+void CHL2MPRules::CreateStandardEntities( void )
+{
+
+#ifndef CLIENT_DLL
+ // Create the entity that will send our data to the client.
+
+ BaseClass::CreateStandardEntities();
+
+ g_pLastCombineSpawn = NULL;
+ g_pLastRebelSpawn = NULL;
+
+#ifdef DBGFLAG_ASSERT
+ CBaseEntity *pEnt =
+#endif
+ CBaseEntity::Create( "hl2mp_gamerules", vec3_origin, vec3_angle );
+ Assert( pEnt );
+#endif
+}
+
+//=========================================================
+// FlWeaponRespawnTime - what is the time in the future
+// at which this weapon may spawn?
+//=========================================================
+float CHL2MPRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon )
+{
+#ifndef CLIENT_DLL
+ if ( weaponstay.GetInt() > 0 )
+ {
+ // make sure it's only certain weapons
+ if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
+ {
+ return 0; // weapon respawns almost instantly
+ }
+ }
+
+ return sv_hl2mp_weapon_respawn_time.GetFloat();
+#endif
+
+ return 0; // weapon respawns almost instantly
+}
+
+
+bool CHL2MPRules::IsIntermission( void )
+{
+#ifndef CLIENT_DLL
+ return m_flIntermissionEndTime > gpGlobals->curtime;
+#endif
+
+ return false;
+}
+
+void CHL2MPRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
+{
+#ifndef CLIENT_DLL
+ if ( IsIntermission() )
+ return;
+ BaseClass::PlayerKilled( pVictim, info );
+#endif
+}
+
+
+void CHL2MPRules::Think( void )
+{
+
+#ifndef CLIENT_DLL
+
+ CGameRules::Think();
+
+ if ( g_fGameOver ) // someone else quit the game already
+ {
+ // check to see if we should change levels now
+ if ( m_flIntermissionEndTime < gpGlobals->curtime )
+ {
+ if ( !m_bChangelevelDone )
+ {
+ ChangeLevel(); // intermission is over
+ m_bChangelevelDone = true;
+ }
+ }
+
+ return;
+ }
+
+// float flTimeLimit = mp_timelimit.GetFloat() * 60;
+ float flFragLimit = fraglimit.GetFloat();
+
+ if ( GetMapRemainingTime() < 0 )
+ {
+ GoToIntermission();
+ return;
+ }
+
+ if ( flFragLimit )
+ {
+ if( IsTeamplay() == true )
+ {
+ CTeam *pCombine = g_Teams[TEAM_COMBINE];
+ CTeam *pRebels = g_Teams[TEAM_REBELS];
+
+ if ( pCombine->GetScore() >= flFragLimit || pRebels->GetScore() >= flFragLimit )
+ {
+ GoToIntermission();
+ return;
+ }
+ }
+ else
+ {
+ // check if any player is over the frag limit
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( pPlayer && pPlayer->FragCount() >= flFragLimit )
+ {
+ GoToIntermission();
+ return;
+ }
+ }
+ }
+ }
+
+ if ( gpGlobals->curtime > m_tmNextPeriodicThink )
+ {
+ CheckAllPlayersReady();
+ CheckRestartGame();
+ m_tmNextPeriodicThink = gpGlobals->curtime + 1.0;
+ }
+
+ if ( m_flRestartGameTime > 0.0f && m_flRestartGameTime <= gpGlobals->curtime )
+ {
+ RestartGame();
+ }
+
+ if( m_bAwaitingReadyRestart && m_bHeardAllPlayersReady )
+ {
+ UTIL_ClientPrintAll( HUD_PRINTCENTER, "All players ready. Game will restart in 5 seconds" );
+ UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "All players ready. Game will restart in 5 seconds" );
+
+ m_flRestartGameTime = gpGlobals->curtime + 5;
+ m_bAwaitingReadyRestart = false;
+ }
+
+ ManageObjectRelocation();
+
+#endif
+}
+
+void CHL2MPRules::GoToIntermission( void )
+{
+#ifndef CLIENT_DLL
+ if ( g_fGameOver )
+ return;
+
+ g_fGameOver = true;
+
+ m_flIntermissionEndTime = gpGlobals->curtime + mp_chattime.GetInt();
+
+ for ( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD );
+ pPlayer->AddFlag( FL_FROZEN );
+ }
+#endif
+
+}
+
+bool CHL2MPRules::CheckGameOver()
+{
+#ifndef CLIENT_DLL
+ if ( g_fGameOver ) // someone else quit the game already
+ {
+ // check to see if we should change levels now
+ if ( m_flIntermissionEndTime < gpGlobals->curtime )
+ {
+ ChangeLevel(); // intermission is over
+ }
+
+ return true;
+ }
+#endif
+
+ return false;
+}
+
+// when we are within this close to running out of entities, items
+// marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn
+#define ENTITY_INTOLERANCE 100
+
+//=========================================================
+// FlWeaponRespawnTime - Returns 0 if the weapon can respawn
+// now, otherwise it returns the time at which it can try
+// to spawn again.
+//=========================================================
+float CHL2MPRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon )
+{
+#ifndef CLIENT_DLL
+ if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
+ {
+ if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) )
+ return 0;
+
+ // we're past the entity tolerance level, so delay the respawn
+ return FlWeaponRespawnTime( pWeapon );
+ }
+#endif
+ return 0;
+}
+
+//=========================================================
+// VecWeaponRespawnSpot - where should this weapon spawn?
+// Some game variations may choose to randomize spawn locations
+//=========================================================
+Vector CHL2MPRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon )
+{
+#ifndef CLIENT_DLL
+ CWeaponHL2MPBase *pHL2Weapon = dynamic_cast< CWeaponHL2MPBase*>( pWeapon );
+
+ if ( pHL2Weapon )
+ {
+ return pHL2Weapon->GetOriginalSpawnOrigin();
+ }
+#endif
+
+ return pWeapon->GetAbsOrigin();
+}
+
+#ifndef CLIENT_DLL
+
+CItem* IsManagedObjectAnItem( CBaseEntity *pObject )
+{
+ return dynamic_cast< CItem*>( pObject );
+}
+
+CWeaponHL2MPBase* IsManagedObjectAWeapon( CBaseEntity *pObject )
+{
+ return dynamic_cast< CWeaponHL2MPBase*>( pObject );
+}
+
+bool GetObjectsOriginalParameters( CBaseEntity *pObject, Vector &vOriginalOrigin, QAngle &vOriginalAngles )
+{
+ if ( CItem *pItem = IsManagedObjectAnItem( pObject ) )
+ {
+ if ( pItem->m_flNextResetCheckTime > gpGlobals->curtime )
+ return false;
+
+ vOriginalOrigin = pItem->GetOriginalSpawnOrigin();
+ vOriginalAngles = pItem->GetOriginalSpawnAngles();
+
+ pItem->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_item_respawn_time.GetFloat();
+ return true;
+ }
+ else if ( CWeaponHL2MPBase *pWeapon = IsManagedObjectAWeapon( pObject ))
+ {
+ if ( pWeapon->m_flNextResetCheckTime > gpGlobals->curtime )
+ return false;
+
+ vOriginalOrigin = pWeapon->GetOriginalSpawnOrigin();
+ vOriginalAngles = pWeapon->GetOriginalSpawnAngles();
+
+ pWeapon->m_flNextResetCheckTime = gpGlobals->curtime + sv_hl2mp_weapon_respawn_time.GetFloat();
+ return true;
+ }
+
+ return false;
+}
+
+void CHL2MPRules::ManageObjectRelocation( void )
+{
+ int iTotal = m_hRespawnableItemsAndWeapons.Count();
+
+ if ( iTotal > 0 )
+ {
+ for ( int i = 0; i < iTotal; i++ )
+ {
+ CBaseEntity *pObject = m_hRespawnableItemsAndWeapons[i].Get();
+
+ if ( pObject )
+ {
+ Vector vSpawOrigin;
+ QAngle vSpawnAngles;
+
+ if ( GetObjectsOriginalParameters( pObject, vSpawOrigin, vSpawnAngles ) == true )
+ {
+ float flDistanceFromSpawn = (pObject->GetAbsOrigin() - vSpawOrigin ).Length();
+
+ if ( flDistanceFromSpawn > WEAPON_MAX_DISTANCE_FROM_SPAWN )
+ {
+ bool shouldReset = false;
+ IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
+
+ if ( pPhysics )
+ {
+ shouldReset = pPhysics->IsAsleep();
+ }
+ else
+ {
+ shouldReset = (pObject->GetFlags() & FL_ONGROUND) ? true : false;
+ }
+
+ if ( shouldReset )
+ {
+ pObject->Teleport( &vSpawOrigin, &vSpawnAngles, NULL );
+ pObject->EmitSound( "AlyxEmp.Charge" );
+
+ IPhysicsObject *pPhys = pObject->VPhysicsGetObject();
+
+ if ( pPhys )
+ {
+ pPhys->Wake();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//=========================================================
+//AddLevelDesignerPlacedWeapon
+//=========================================================
+void CHL2MPRules::AddLevelDesignerPlacedObject( CBaseEntity *pEntity )
+{
+ if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) == -1 )
+ {
+ m_hRespawnableItemsAndWeapons.AddToTail( pEntity );
+ }
+}
+
+//=========================================================
+//RemoveLevelDesignerPlacedWeapon
+//=========================================================
+void CHL2MPRules::RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity )
+{
+ if ( m_hRespawnableItemsAndWeapons.Find( pEntity ) != -1 )
+ {
+ m_hRespawnableItemsAndWeapons.FindAndRemove( pEntity );
+ }
+}
+
+//=========================================================
+// Where should this item respawn?
+// Some game variations may choose to randomize spawn locations
+//=========================================================
+Vector CHL2MPRules::VecItemRespawnSpot( CItem *pItem )
+{
+ return pItem->GetOriginalSpawnOrigin();
+}
+
+//=========================================================
+// What angles should this item use to respawn?
+//=========================================================
+QAngle CHL2MPRules::VecItemRespawnAngles( CItem *pItem )
+{
+ return pItem->GetOriginalSpawnAngles();
+}
+
+//=========================================================
+// At what time in the future may this Item respawn?
+//=========================================================
+float CHL2MPRules::FlItemRespawnTime( CItem *pItem )
+{
+ return sv_hl2mp_item_respawn_time.GetFloat();
+}
+
+
+//=========================================================
+// CanHaveWeapon - returns false if the player is not allowed
+// to pick up this weapon
+//=========================================================
+bool CHL2MPRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem )
+{
+ if ( weaponstay.GetInt() > 0 )
+ {
+ if ( pPlayer->Weapon_OwnsThisType( pItem->GetClassname(), pItem->GetSubType() ) )
+ return false;
+ }
+
+ return BaseClass::CanHavePlayerItem( pPlayer, pItem );
+}
+
+#endif
+
+//=========================================================
+// WeaponShouldRespawn - any conditions inhibiting the
+// respawning of this weapon?
+//=========================================================
+int CHL2MPRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon )
+{
+#ifndef CLIENT_DLL
+ if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) )
+ {
+ return GR_WEAPON_RESPAWN_NO;
+ }
+#endif
+
+ return GR_WEAPON_RESPAWN_YES;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player has just left the game
+//-----------------------------------------------------------------------------
+void CHL2MPRules::ClientDisconnected( edict_t *pClient )
+{
+#ifndef CLIENT_DLL
+ // Msg( "CLIENT DISCONNECTED, REMOVING FROM TEAM.\n" );
+
+ CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
+ if ( pPlayer )
+ {
+ // Remove the player from his team
+ if ( pPlayer->GetTeam() )
+ {
+ pPlayer->GetTeam()->RemovePlayer( pPlayer );
+ }
+ }
+
+ BaseClass::ClientDisconnected( pClient );
+
+#endif
+}
+
+
+//=========================================================
+// Deathnotice.
+//=========================================================
+void CHL2MPRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
+{
+#ifndef CLIENT_DLL
+ // Work out what killed the player, and send a message to all clients about it
+ const char *killer_weapon_name = "world"; // by default, the player is killed by the world
+ int killer_ID = 0;
+
+ // Find the killer & the scorer
+ CBaseEntity *pInflictor = info.GetInflictor();
+ CBaseEntity *pKiller = info.GetAttacker();
+ CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
+
+ // Custom kill type?
+ if ( info.GetDamageCustom() )
+ {
+ killer_weapon_name = GetDamageCustomString( info );
+ if ( pScorer )
+ {
+ killer_ID = pScorer->GetUserID();
+ }
+ }
+ else
+ {
+ // Is the killer a client?
+ if ( pScorer )
+ {
+ killer_ID = pScorer->GetUserID();
+
+ if ( pInflictor )
+ {
+ if ( pInflictor == pScorer )
+ {
+ // If the inflictor is the killer, then it must be their current weapon doing the damage
+ if ( pScorer->GetActiveWeapon() )
+ {
+ killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname();
+ }
+ }
+ else
+ {
+ killer_weapon_name = pInflictor->GetClassname(); // it's just that easy
+ }
+ }
+ }
+ else
+ {
+ killer_weapon_name = pInflictor->GetClassname();
+ }
+
+ // strip the NPC_* or weapon_* from the inflictor's classname
+ if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 )
+ {
+ killer_weapon_name += 7;
+ }
+ else if ( strncmp( killer_weapon_name, "npc_", 4 ) == 0 )
+ {
+ killer_weapon_name += 4;
+ }
+ else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 )
+ {
+ killer_weapon_name += 5;
+ }
+ else if ( strstr( killer_weapon_name, "physics" ) )
+ {
+ killer_weapon_name = "physics";
+ }
+
+ if ( strcmp( killer_weapon_name, "prop_combine_ball" ) == 0 )
+ {
+ killer_weapon_name = "combine_ball";
+ }
+ else if ( strcmp( killer_weapon_name, "grenade_ar2" ) == 0 )
+ {
+ killer_weapon_name = "smg1_grenade";
+ }
+ else if ( strcmp( killer_weapon_name, "satchel" ) == 0 || strcmp( killer_weapon_name, "tripmine" ) == 0)
+ {
+ killer_weapon_name = "slam";
+ }
+
+
+ }
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "player_death" );
+ if( event )
+ {
+ event->SetInt("userid", pVictim->GetUserID() );
+ event->SetInt("attacker", killer_ID );
+ event->SetString("weapon", killer_weapon_name );
+ event->SetInt( "priority", 7 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+}
+
+void CHL2MPRules::ClientSettingsChanged( CBasePlayer *pPlayer )
+{
+#ifndef CLIENT_DLL
+
+ CHL2MP_Player *pHL2Player = ToHL2MPPlayer( pPlayer );
+
+ if ( pHL2Player == NULL )
+ return;
+
+ const char *pCurrentModel = modelinfo->GetModelName( pPlayer->GetModel() );
+ const char *szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_playermodel" );
+
+ //If we're different.
+ if ( stricmp( szModelName, pCurrentModel ) )
+ {
+ //Too soon, set the cvar back to what it was.
+ //Note: this will make this function be called again
+ //but since our models will match it'll just skip this whole dealio.
+ if ( pHL2Player->GetNextModelChangeTime() >= gpGlobals->curtime )
+ {
+ char szReturnString[512];
+
+ Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pCurrentModel );
+ engine->ClientCommand ( pHL2Player->edict(), szReturnString );
+
+ Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch.\n", (int)(pHL2Player->GetNextModelChangeTime() - gpGlobals->curtime) );
+ ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString );
+ return;
+ }
+
+ if ( HL2MPRules()->IsTeamplay() == false )
+ {
+ pHL2Player->SetPlayerModel();
+
+ const char *pszCurrentModelName = modelinfo->GetModelName( pHL2Player->GetModel() );
+
+ char szReturnString[128];
+ Q_snprintf( szReturnString, sizeof( szReturnString ), "Your player model is: %s\n", pszCurrentModelName );
+
+ ClientPrint( pHL2Player, HUD_PRINTTALK, szReturnString );
+ }
+ else
+ {
+ if ( Q_stristr( szModelName, "models/human") )
+ {
+ pHL2Player->ChangeTeam( TEAM_REBELS );
+ }
+ else
+ {
+ pHL2Player->ChangeTeam( TEAM_COMBINE );
+ }
+ }
+ }
+ if ( sv_report_client_settings.GetInt() == 1 )
+ {
+ UTIL_LogPrintf( "\"%s\" cl_cmdrate = \"%s\"\n", pHL2Player->GetPlayerName(), engine->GetClientConVarValue( pHL2Player->entindex(), "cl_cmdrate" ));
+ }
+
+ BaseClass::ClientSettingsChanged( pPlayer );
+#endif
+
+}
+
+int CHL2MPRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
+{
+#ifndef CLIENT_DLL
+ // half life multiplay has a simple concept of Player Relationships.
+ // you are either on another player's team, or you are not.
+ if ( !pPlayer || !pTarget || !pTarget->IsPlayer() || IsTeamplay() == false )
+ return GR_NOTTEAMMATE;
+
+ if ( (*GetTeamID(pPlayer) != '\0') && (*GetTeamID(pTarget) != '\0') && !stricmp( GetTeamID(pPlayer), GetTeamID(pTarget) ) )
+ {
+ return GR_TEAMMATE;
+ }
+#endif
+
+ return GR_NOTTEAMMATE;
+}
+
+const char *CHL2MPRules::GetGameDescription( void )
+{
+ if ( IsTeamplay() )
+ return "Team Deathmatch";
+
+ return "Deathmatch";
+}
+
+bool CHL2MPRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer )
+{
+ return true;
+}
+
+float CHL2MPRules::GetMapRemainingTime()
+{
+ // if timelimit is disabled, return 0
+ if ( mp_timelimit.GetInt() <= 0 )
+ return 0;
+
+ // timelimit is in minutes
+
+ float timeleft = (m_flGameStartTime + mp_timelimit.GetInt() * 60.0f ) - gpGlobals->curtime;
+
+ return timeleft;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHL2MPRules::Precache( void )
+{
+ CBaseEntity::PrecacheScriptSound( "AlyxEmp.Charge" );
+}
+
+bool CHL2MPRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
+{
+ if ( collisionGroup0 > collisionGroup1 )
+ {
+ // swap so that lowest is always first
+ V_swap(collisionGroup0,collisionGroup1);
+ }
+
+ if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
+ collisionGroup1 == COLLISION_GROUP_WEAPON )
+ {
+ return false;
+ }
+
+ return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
+
+}
+
+bool CHL2MPRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
+{
+#ifndef CLIENT_DLL
+ if( BaseClass::ClientCommand( pEdict, args ) )
+ return true;
+
+
+ CHL2MP_Player *pPlayer = (CHL2MP_Player *) pEdict;
+
+ if ( pPlayer->ClientCommand( args ) )
+ return true;
+#endif
+
+ return false;
+}
+
+// shared ammo definition
+// JAY: Trying to make a more physical bullet response
+#define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f)
+#define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
+
+// exaggerate all of the forces, but use real numbers to keep them consistent
+#define BULLET_IMPULSE_EXAGGERATION 3.5
+// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
+#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
+
+
+CAmmoDef *GetAmmoDef()
+{
+ static CAmmoDef def;
+ static bool bInitted = false;
+
+ if ( !bInitted )
+ {
+ bInitted = true;
+
+ def.AddAmmoType("AR2", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 60, BULLET_IMPULSE(200, 1225), 0 );
+ def.AddAmmoType("AR2AltFire", DMG_DISSOLVE, TRACER_NONE, 0, 0, 3, 0, 0 );
+ def.AddAmmoType("Pistol", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 150, BULLET_IMPULSE(200, 1225), 0 );
+ def.AddAmmoType("SMG1", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 225, BULLET_IMPULSE(200, 1225), 0 );
+ def.AddAmmoType("357", DMG_BULLET, TRACER_LINE_AND_WHIZ, 0, 0, 12, BULLET_IMPULSE(800, 5000), 0 );
+ def.AddAmmoType("XBowBolt", DMG_BULLET, TRACER_LINE, 0, 0, 10, BULLET_IMPULSE(800, 8000), 0 );
+ def.AddAmmoType("Buckshot", DMG_BULLET | DMG_BUCKSHOT, TRACER_LINE, 0, 0, 30, BULLET_IMPULSE(400, 1200), 0 );
+ def.AddAmmoType("RPG_Round", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 );
+ def.AddAmmoType("SMG1_Grenade", DMG_BURN, TRACER_NONE, 0, 0, 3, 0, 0 );
+ def.AddAmmoType("Grenade", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 );
+ def.AddAmmoType("slam", DMG_BURN, TRACER_NONE, 0, 0, 5, 0, 0 );
+ }
+
+ return &def;
+}
+
+#ifdef CLIENT_DLL
+
+ ConVar cl_autowepswitch(
+ "cl_autowepswitch",
+ "1",
+ FCVAR_ARCHIVE | FCVAR_USERINFO,
+ "Automatically switch to picked up weapons (if more powerful)" );
+
+#else
+
+#ifdef DEBUG
+
+ // Handler for the "bot" command.
+ void Bot_f()
+ {
+ // Look at -count.
+ int count = 1;
+ count = clamp( count, 1, 16 );
+
+ int iTeam = TEAM_COMBINE;
+
+ // Look at -frozen.
+ bool bFrozen = false;
+
+ // Ok, spawn all the bots.
+ while ( --count >= 0 )
+ {
+ BotPutInServer( bFrozen, iTeam );
+ }
+ }
+
+
+ ConCommand cc_Bot( "bot", Bot_f, "Add a bot.", FCVAR_CHEAT );
+
+#endif
+
+ bool CHL2MPRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
+ {
+ if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() )
+ {
+ // Player has an active item, so let's check cl_autowepswitch.
+ const char *cl_autowepswitch = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_autowepswitch" );
+ if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 )
+ {
+ return false;
+ }
+ }
+
+ return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon );
+ }
+
+#endif
+
+#ifndef CLIENT_DLL
+
+void CHL2MPRules::RestartGame()
+{
+ // bounds check
+ if ( mp_timelimit.GetInt() < 0 )
+ {
+ mp_timelimit.SetValue( 0 );
+ }
+ m_flGameStartTime = gpGlobals->curtime;
+ if ( !IsFinite( m_flGameStartTime.Get() ) )
+ {
+ Warning( "Trying to set a NaN game start time\n" );
+ m_flGameStartTime.GetForModify() = 0.0f;
+ }
+
+ CleanUpMap();
+
+ // now respawn all players
+ for (int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ if ( pPlayer->GetActiveWeapon() )
+ {
+ pPlayer->GetActiveWeapon()->Holster();
+ }
+ pPlayer->RemoveAllItems( true );
+ respawn( pPlayer, false );
+ pPlayer->Reset();
+ }
+
+ // Respawn entities (glass, doors, etc..)
+
+ CTeam *pRebels = GetGlobalTeam( TEAM_REBELS );
+ CTeam *pCombine = GetGlobalTeam( TEAM_COMBINE );
+
+ if ( pRebels )
+ {
+ pRebels->SetScore( 0 );
+ }
+
+ if ( pCombine )
+ {
+ pCombine->SetScore( 0 );
+ }
+
+ m_flIntermissionEndTime = 0;
+ m_flRestartGameTime = 0.0;
+ m_bCompleteReset = false;
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "round_start" );
+ if ( event )
+ {
+ event->SetInt("fraglimit", 0 );
+ event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
+
+ event->SetString("objective","DEATHMATCH");
+
+ gameeventmanager->FireEvent( event );
+ }
+}
+
+void CHL2MPRules::CleanUpMap()
+{
+ // Recreate all the map entities from the map data (preserving their indices),
+ // then remove everything else except the players.
+
+ // Get rid of all entities except players.
+ CBaseEntity *pCur = gEntList.FirstEnt();
+ while ( pCur )
+ {
+ CBaseHL2MPCombatWeapon *pWeapon = dynamic_cast< CBaseHL2MPCombatWeapon* >( pCur );
+ // Weapons with owners don't want to be removed..
+ if ( pWeapon )
+ {
+ if ( !pWeapon->GetPlayerOwner() )
+ {
+ UTIL_Remove( pCur );
+ }
+ }
+ // remove entities that has to be restored on roundrestart (breakables etc)
+ else if ( !FindInList( s_PreserveEnts, pCur->GetClassname() ) )
+ {
+ UTIL_Remove( pCur );
+ }
+
+ pCur = gEntList.NextEnt( pCur );
+ }
+
+ // Really remove the entities so we can have access to their slots below.
+ gEntList.CleanupDeleteList();
+
+ // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that
+ // could kill respawning CTs
+ g_EventQueue.Clear();
+
+ // Now reload the map entities.
+ class CHL2MPMapEntityFilter : public IMapEntityFilter
+ {
+ public:
+ virtual bool ShouldCreateEntity( const char *pClassname )
+ {
+ // Don't recreate the preserved entities.
+ if ( !FindInList( s_PreserveEnts, pClassname ) )
+ {
+ return true;
+ }
+ else
+ {
+ // Increment our iterator since it's not going to call CreateNextEntity for this ent.
+ if ( m_iIterator != g_MapEntityRefs.InvalidIndex() )
+ m_iIterator = g_MapEntityRefs.Next( m_iIterator );
+
+ return false;
+ }
+ }
+
+
+ virtual CBaseEntity* CreateNextEntity( const char *pClassname )
+ {
+ if ( m_iIterator == g_MapEntityRefs.InvalidIndex() )
+ {
+ // This shouldn't be possible. When we loaded the map, it should have used
+ // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list
+ // with the same list of entities we're referring to here.
+ Assert( false );
+ return NULL;
+ }
+ else
+ {
+ CMapEntityRef &ref = g_MapEntityRefs[m_iIterator];
+ m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity.
+
+ if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) )
+ {
+ // Doh! The entity was delete and its slot was reused.
+ // Just use any old edict slot. This case sucks because we lose the baseline.
+ return CreateEntityByName( pClassname );
+ }
+ else
+ {
+ // Cool, the slot where this entity was is free again (most likely, the entity was
+ // freed above). Now create an entity with this specific index.
+ return CreateEntityByName( pClassname, ref.m_iEdict );
+ }
+ }
+ }
+
+ public:
+ int m_iIterator; // Iterator into g_MapEntityRefs.
+ };
+ CHL2MPMapEntityFilter filter;
+ filter.m_iIterator = g_MapEntityRefs.Head();
+
+ // DO NOT CALL SPAWN ON info_node ENTITIES!
+
+ MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
+}
+
+void CHL2MPRules::CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg )
+{
+ if( m_bAwaitingReadyRestart && FStrEq( chatmsg, mp_ready_signal.GetString() ) )
+ {
+ if( !pPlayer->IsReady() )
+ {
+ pPlayer->SetReady( true );
+ }
+ }
+}
+
+void CHL2MPRules::CheckRestartGame( void )
+{
+ // Restart the game if specified by the server
+ int iRestartDelay = mp_restartgame.GetInt();
+
+ if ( iRestartDelay > 0 )
+ {
+ if ( iRestartDelay > 60 )
+ iRestartDelay = 60;
+
+
+ // let the players know
+ char strRestartDelay[64];
+ Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay );
+ UTIL_ClientPrintAll( HUD_PRINTCENTER, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
+ UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "Game will restart in %s1 %s2", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
+
+ m_flRestartGameTime = gpGlobals->curtime + iRestartDelay;
+ m_bCompleteReset = true;
+ mp_restartgame.SetValue( 0 );
+ }
+
+ if( mp_readyrestart.GetBool() )
+ {
+ m_bAwaitingReadyRestart = true;
+ m_bHeardAllPlayersReady = false;
+
+
+ const char *pszReadyString = mp_ready_signal.GetString();
+
+
+ // Don't let them put anything malicious in there
+ if( pszReadyString == NULL || Q_strlen(pszReadyString) > 16 )
+ {
+ pszReadyString = "ready";
+ }
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "hl2mp_ready_restart" );
+ if ( event )
+ gameeventmanager->FireEvent( event );
+
+ mp_readyrestart.SetValue( 0 );
+
+ // cancel any restart round in progress
+ m_flRestartGameTime = -1;
+ }
+}
+
+void CHL2MPRules::CheckAllPlayersReady( void )
+{
+ for (int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CHL2MP_Player *pPlayer = (CHL2MP_Player*) UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+ if ( !pPlayer->IsReady() )
+ return;
+ }
+ m_bHeardAllPlayersReady = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CHL2MPRules::GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer )
+{
+ if ( !pPlayer ) // dedicated server output
+ {
+ return NULL;
+ }
+
+ const char *pszFormat = NULL;
+
+ // team only
+ if ( bTeamOnly == TRUE )
+ {
+ if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
+ {
+ pszFormat = "HL2MP_Chat_Spec";
+ }
+ else
+ {
+ const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
+ if ( chatLocation && *chatLocation )
+ {
+ pszFormat = "HL2MP_Chat_Team_Loc";
+ }
+ else
+ {
+ pszFormat = "HL2MP_Chat_Team";
+ }
+ }
+ }
+ // everyone
+ else
+ {
+ if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
+ {
+ pszFormat = "HL2MP_Chat_All";
+ }
+ else
+ {
+ pszFormat = "HL2MP_Chat_AllSpec";
+ }
+ }
+
+ return pszFormat;
+}
+
#endif \ No newline at end of file
diff --git a/mp/src/game/shared/hl2mp/hl2mp_gamerules.h b/mp/src/game/shared/hl2mp/hl2mp_gamerules.h
index 8cadeab0..8d91554c 100644
--- a/mp/src/game/shared/hl2mp/hl2mp_gamerules.h
+++ b/mp/src/game/shared/hl2mp/hl2mp_gamerules.h
@@ -1,172 +1,172 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-//
-//-----------------------------------------------------------------------------
-// $Log: $
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef HL2MP_GAMERULES_H
-#define HL2MP_GAMERULES_H
-#pragma once
-
-#include "gamerules.h"
-#include "teamplay_gamerules.h"
-#include "gamevars_shared.h"
-
-#ifndef CLIENT_DLL
-#include "hl2mp_player.h"
-#endif
-
-#define VEC_CROUCH_TRACE_MIN HL2MPRules()->GetHL2MPViewVectors()->m_vCrouchTraceMin
-#define VEC_CROUCH_TRACE_MAX HL2MPRules()->GetHL2MPViewVectors()->m_vCrouchTraceMax
-
-enum
-{
- TEAM_COMBINE = 2,
- TEAM_REBELS,
-};
-
-
-#ifdef CLIENT_DLL
- #define CHL2MPRules C_HL2MPRules
- #define CHL2MPGameRulesProxy C_HL2MPGameRulesProxy
-#endif
-
-class CHL2MPGameRulesProxy : public CGameRulesProxy
-{
-public:
- DECLARE_CLASS( CHL2MPGameRulesProxy, CGameRulesProxy );
- DECLARE_NETWORKCLASS();
-};
-
-class HL2MPViewVectors : public CViewVectors
-{
-public:
- HL2MPViewVectors(
- Vector vView,
- Vector vHullMin,
- Vector vHullMax,
- Vector vDuckHullMin,
- Vector vDuckHullMax,
- Vector vDuckView,
- Vector vObsHullMin,
- Vector vObsHullMax,
- Vector vDeadViewHeight,
- Vector vCrouchTraceMin,
- Vector vCrouchTraceMax ) :
- CViewVectors(
- vView,
- vHullMin,
- vHullMax,
- vDuckHullMin,
- vDuckHullMax,
- vDuckView,
- vObsHullMin,
- vObsHullMax,
- vDeadViewHeight )
- {
- m_vCrouchTraceMin = vCrouchTraceMin;
- m_vCrouchTraceMax = vCrouchTraceMax;
- }
-
- Vector m_vCrouchTraceMin;
- Vector m_vCrouchTraceMax;
-};
-
-class CHL2MPRules : public CTeamplayRules
-{
-public:
- DECLARE_CLASS( CHL2MPRules, CTeamplayRules );
-
-#ifdef CLIENT_DLL
-
- DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars.
-
-#else
-
- DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars.
-#endif
-
- CHL2MPRules();
- virtual ~CHL2MPRules();
-
- virtual void Precache( void );
- virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 );
- virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args );
-
- virtual float FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon );
- virtual float FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon );
- virtual Vector VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon );
- virtual int WeaponShouldRespawn( CBaseCombatWeapon *pWeapon );
- virtual void Think( void );
- virtual void CreateStandardEntities( void );
- virtual void ClientSettingsChanged( CBasePlayer *pPlayer );
- virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget );
- virtual void GoToIntermission( void );
- virtual void DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info );
- virtual const char *GetGameDescription( void );
- // derive this function if you mod uses encrypted weapon info files
- virtual const unsigned char *GetEncryptionKey( void ) { return (unsigned char *)"x9Ke0BY7"; }
- virtual const CViewVectors* GetViewVectors() const;
- const HL2MPViewVectors* GetHL2MPViewVectors() const;
-
- float GetMapRemainingTime();
- void CleanUpMap();
- void CheckRestartGame();
- void RestartGame();
-
-#ifndef CLIENT_DLL
- virtual Vector VecItemRespawnSpot( CItem *pItem );
- virtual QAngle VecItemRespawnAngles( CItem *pItem );
- virtual float FlItemRespawnTime( CItem *pItem );
- virtual bool CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem );
- virtual bool FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon );
-
- void AddLevelDesignerPlacedObject( CBaseEntity *pEntity );
- void RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity );
- void ManageObjectRelocation( void );
- void CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg );
- const char *GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer );
-
-#endif
- virtual void ClientDisconnected( edict_t *pClient );
-
- bool CheckGameOver( void );
- bool IsIntermission( void );
-
- void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info );
-
-
- bool IsTeamplay( void ) { return m_bTeamPlayEnabled; }
- void CheckAllPlayersReady( void );
-
- virtual bool IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer );
-
-private:
-
- CNetworkVar( bool, m_bTeamPlayEnabled );
- CNetworkVar( float, m_flGameStartTime );
- CUtlVector<EHANDLE> m_hRespawnableItemsAndWeapons;
- float m_tmNextPeriodicThink;
- float m_flRestartGameTime;
- bool m_bCompleteReset;
- bool m_bAwaitingReadyRestart;
- bool m_bHeardAllPlayersReady;
-
-#ifndef CLIENT_DLL
- bool m_bChangelevelDone;
-#endif
-};
-
-inline CHL2MPRules* HL2MPRules()
-{
- return static_cast<CHL2MPRules*>(g_pGameRules);
-}
-
-#endif //HL2MP_GAMERULES_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef HL2MP_GAMERULES_H
+#define HL2MP_GAMERULES_H
+#pragma once
+
+#include "gamerules.h"
+#include "teamplay_gamerules.h"
+#include "gamevars_shared.h"
+
+#ifndef CLIENT_DLL
+#include "hl2mp_player.h"
+#endif
+
+#define VEC_CROUCH_TRACE_MIN HL2MPRules()->GetHL2MPViewVectors()->m_vCrouchTraceMin
+#define VEC_CROUCH_TRACE_MAX HL2MPRules()->GetHL2MPViewVectors()->m_vCrouchTraceMax
+
+enum
+{
+ TEAM_COMBINE = 2,
+ TEAM_REBELS,
+};
+
+
+#ifdef CLIENT_DLL
+ #define CHL2MPRules C_HL2MPRules
+ #define CHL2MPGameRulesProxy C_HL2MPGameRulesProxy
+#endif
+
+class CHL2MPGameRulesProxy : public CGameRulesProxy
+{
+public:
+ DECLARE_CLASS( CHL2MPGameRulesProxy, CGameRulesProxy );
+ DECLARE_NETWORKCLASS();
+};
+
+class HL2MPViewVectors : public CViewVectors
+{
+public:
+ HL2MPViewVectors(
+ Vector vView,
+ Vector vHullMin,
+ Vector vHullMax,
+ Vector vDuckHullMin,
+ Vector vDuckHullMax,
+ Vector vDuckView,
+ Vector vObsHullMin,
+ Vector vObsHullMax,
+ Vector vDeadViewHeight,
+ Vector vCrouchTraceMin,
+ Vector vCrouchTraceMax ) :
+ CViewVectors(
+ vView,
+ vHullMin,
+ vHullMax,
+ vDuckHullMin,
+ vDuckHullMax,
+ vDuckView,
+ vObsHullMin,
+ vObsHullMax,
+ vDeadViewHeight )
+ {
+ m_vCrouchTraceMin = vCrouchTraceMin;
+ m_vCrouchTraceMax = vCrouchTraceMax;
+ }
+
+ Vector m_vCrouchTraceMin;
+ Vector m_vCrouchTraceMax;
+};
+
+class CHL2MPRules : public CTeamplayRules
+{
+public:
+ DECLARE_CLASS( CHL2MPRules, CTeamplayRules );
+
+#ifdef CLIENT_DLL
+
+ DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars.
+
+#else
+
+ DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars.
+#endif
+
+ CHL2MPRules();
+ virtual ~CHL2MPRules();
+
+ virtual void Precache( void );
+ virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 );
+ virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args );
+
+ virtual float FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon );
+ virtual float FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon );
+ virtual Vector VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon );
+ virtual int WeaponShouldRespawn( CBaseCombatWeapon *pWeapon );
+ virtual void Think( void );
+ virtual void CreateStandardEntities( void );
+ virtual void ClientSettingsChanged( CBasePlayer *pPlayer );
+ virtual int PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget );
+ virtual void GoToIntermission( void );
+ virtual void DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info );
+ virtual const char *GetGameDescription( void );
+ // derive this function if you mod uses encrypted weapon info files
+ virtual const unsigned char *GetEncryptionKey( void ) { return (unsigned char *)"x9Ke0BY7"; }
+ virtual const CViewVectors* GetViewVectors() const;
+ const HL2MPViewVectors* GetHL2MPViewVectors() const;
+
+ float GetMapRemainingTime();
+ void CleanUpMap();
+ void CheckRestartGame();
+ void RestartGame();
+
+#ifndef CLIENT_DLL
+ virtual Vector VecItemRespawnSpot( CItem *pItem );
+ virtual QAngle VecItemRespawnAngles( CItem *pItem );
+ virtual float FlItemRespawnTime( CItem *pItem );
+ virtual bool CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem );
+ virtual bool FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon );
+
+ void AddLevelDesignerPlacedObject( CBaseEntity *pEntity );
+ void RemoveLevelDesignerPlacedObject( CBaseEntity *pEntity );
+ void ManageObjectRelocation( void );
+ void CheckChatForReadySignal( CHL2MP_Player *pPlayer, const char *chatmsg );
+ const char *GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer );
+
+#endif
+ virtual void ClientDisconnected( edict_t *pClient );
+
+ bool CheckGameOver( void );
+ bool IsIntermission( void );
+
+ void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info );
+
+
+ bool IsTeamplay( void ) { return m_bTeamPlayEnabled; }
+ void CheckAllPlayersReady( void );
+
+ virtual bool IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer );
+
+private:
+
+ CNetworkVar( bool, m_bTeamPlayEnabled );
+ CNetworkVar( float, m_flGameStartTime );
+ CUtlVector<EHANDLE> m_hRespawnableItemsAndWeapons;
+ float m_tmNextPeriodicThink;
+ float m_flRestartGameTime;
+ bool m_bCompleteReset;
+ bool m_bAwaitingReadyRestart;
+ bool m_bHeardAllPlayersReady;
+
+#ifndef CLIENT_DLL
+ bool m_bChangelevelDone;
+#endif
+};
+
+inline CHL2MPRules* HL2MPRules()
+{
+ return static_cast<CHL2MPRules*>(g_pGameRules);
+}
+
+#endif //HL2MP_GAMERULES_H
diff --git a/mp/src/game/shared/hl2mp/hl2mp_player_shared.cpp b/mp/src/game/shared/hl2mp/hl2mp_player_shared.cpp
index ff8d28f2..503d498c 100644
--- a/mp/src/game/shared/hl2mp/hl2mp_player_shared.cpp
+++ b/mp/src/game/shared/hl2mp/hl2mp_player_shared.cpp
@@ -1,577 +1,577 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#include "cbase.h"
-
-#ifdef CLIENT_DLL
-#include "c_hl2mp_player.h"
-#include "prediction.h"
-#define CRecipientFilter C_RecipientFilter
-#else
-#include "hl2mp_player.h"
-#endif
-
-#include "engine/IEngineSound.h"
-#include "SoundEmitterSystem/isoundemittersystembase.h"
-
-extern ConVar sv_footsteps;
-
-const char *g_ppszPlayerSoundPrefixNames[PLAYER_SOUNDS_MAX] =
-{
- "NPC_Citizen",
- "NPC_CombineS",
- "NPC_MetroPolice",
-};
-
-const char *CHL2MP_Player::GetPlayerModelSoundPrefix( void )
-{
- return g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType];
-}
-
-void CHL2MP_Player::PrecacheFootStepSounds( void )
-{
- int iFootstepSounds = ARRAYSIZE( g_ppszPlayerSoundPrefixNames );
- int i;
-
- for ( i = 0; i < iFootstepSounds; ++i )
- {
- char szFootStepName[128];
-
- Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[i] );
- PrecacheScriptSound( szFootStepName );
-
- Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[i] );
- PrecacheScriptSound( szFootStepName );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Consider the weapon's built-in accuracy, this character's proficiency with
-// the weapon, and the status of the target. Use this information to determine
-// how accurately to shoot at the target.
-//-----------------------------------------------------------------------------
-Vector CHL2MP_Player::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget )
-{
- if ( pWeapon )
- return pWeapon->GetBulletSpread( WEAPON_PROFICIENCY_PERFECT );
-
- return VECTOR_CONE_15DEGREES;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : step -
-// fvol -
-// force - force sound to play
-//-----------------------------------------------------------------------------
-void CHL2MP_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
-{
- if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
- return;
-
-#if defined( CLIENT_DLL )
- // during prediction play footstep sounds only once
- if ( !prediction->IsFirstTimePredicted() )
- return;
-#endif
-
- if ( GetFlags() & FL_DUCKING )
- return;
-
- m_Local.m_nStepside = !m_Local.m_nStepside;
-
- char szStepSound[128];
-
- if ( m_Local.m_nStepside )
- {
- Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] );
- }
- else
- {
- Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] );
- }
-
- CSoundParameters params;
- if ( GetParametersForSound( szStepSound, params, NULL ) == false )
- return;
-
- CRecipientFilter filter;
- filter.AddRecipientsByPAS( vecOrigin );
-
-#ifndef CLIENT_DLL
- // im MP, server removed all players in origins PVS, these players
- // generate the footsteps clientside
- if ( gpGlobals->maxClients > 1 )
- filter.RemoveRecipientsByPVS( vecOrigin );
-#endif
-
- EmitSound_t ep;
- ep.m_nChannel = CHAN_BODY;
- ep.m_pSoundName = params.soundname;
- ep.m_flVolume = fvol;
- ep.m_SoundLevel = params.soundlevel;
- ep.m_nFlags = 0;
- ep.m_nPitch = params.pitch;
- ep.m_pOrigin = &vecOrigin;
-
- EmitSound( filter, entindex(), ep );
-}
-
-
-//==========================
-// ANIMATION CODE
-//==========================
-
-
-// Below this many degrees, slow down turning rate linearly
-#define FADE_TURN_DEGREES 45.0f
-// After this, need to start turning feet
-#define MAX_TORSO_ANGLE 90.0f
-// Below this amount, don't play a turning animation/perform IK
-#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f
-
-static ConVar tf2_feetyawrunscale( "tf2_feetyawrunscale", "2", FCVAR_REPLICATED, "Multiplier on tf2_feetyawrate to allow turning faster when running." );
-extern ConVar sv_backspeed;
-extern ConVar mp_feetyawrate;
-extern ConVar mp_facefronttime;
-extern ConVar mp_ik;
-
-CPlayerAnimState::CPlayerAnimState( CHL2MP_Player *outer )
- : m_pOuter( outer )
-{
- m_flGaitYaw = 0.0f;
- m_flGoalFeetYaw = 0.0f;
- m_flCurrentFeetYaw = 0.0f;
- m_flCurrentTorsoYaw = 0.0f;
- m_flLastYaw = 0.0f;
- m_flLastTurnTime = 0.0f;
- m_flTurnCorrectionTime = 0.0f;
-};
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPlayerAnimState::Update()
-{
- m_angRender = GetOuter()->GetLocalAngles();
- m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f;
-
- ComputePoseParam_BodyYaw();
- ComputePoseParam_BodyPitch(GetOuter()->GetModelPtr());
- ComputePoseParam_BodyLookYaw();
-
- ComputePlaybackRate();
-
-#ifdef CLIENT_DLL
- GetOuter()->UpdateLookAt();
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPlayerAnimState::ComputePlaybackRate()
-{
- // Determine ideal playback rate
- Vector vel;
- GetOuterAbsVelocity( vel );
-
- float speed = vel.Length2D();
-
- bool isMoving = ( speed > 0.5f ) ? true : false;
-
- float maxspeed = GetOuter()->GetSequenceGroundSpeed( GetOuter()->GetSequence() );
-
- if ( isMoving && ( maxspeed > 0.0f ) )
- {
- float flFactor = 1.0f;
-
- // Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below
- GetOuter()->SetPlaybackRate( ( speed * flFactor ) / maxspeed );
-
- // BUG BUG:
- // This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed
- }
- else
- {
- GetOuter()->SetPlaybackRate( 1.0f );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : CBasePlayer
-//-----------------------------------------------------------------------------
-CHL2MP_Player *CPlayerAnimState::GetOuter()
-{
- return m_pOuter;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : dt -
-//-----------------------------------------------------------------------------
-void CPlayerAnimState::EstimateYaw( void )
-{
- float dt = gpGlobals->frametime;
-
- if ( !dt )
- {
- return;
- }
-
- Vector est_velocity;
- QAngle angles;
-
- GetOuterAbsVelocity( est_velocity );
-
- angles = GetOuter()->GetLocalAngles();
-
- if ( est_velocity[1] == 0 && est_velocity[0] == 0 )
- {
- float flYawDiff = angles[YAW] - m_flGaitYaw;
- flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360;
- if (flYawDiff > 180)
- flYawDiff -= 360;
- if (flYawDiff < -180)
- flYawDiff += 360;
-
- if (dt < 0.25)
- flYawDiff *= dt * 4;
- else
- flYawDiff *= dt;
-
- m_flGaitYaw += flYawDiff;
- m_flGaitYaw = m_flGaitYaw - (int)(m_flGaitYaw / 360) * 360;
- }
- else
- {
- m_flGaitYaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI);
-
- if (m_flGaitYaw > 180)
- m_flGaitYaw = 180;
- else if (m_flGaitYaw < -180)
- m_flGaitYaw = -180;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override for backpeddling
-// Input : dt -
-//-----------------------------------------------------------------------------
-void CPlayerAnimState::ComputePoseParam_BodyYaw( void )
-{
- int iYaw = GetOuter()->LookupPoseParameter( "move_yaw" );
- if ( iYaw < 0 )
- return;
-
- // view direction relative to movement
- float flYaw;
-
- EstimateYaw();
-
- QAngle angles = GetOuter()->GetLocalAngles();
- float ang = angles[ YAW ];
- if ( ang > 180.0f )
- {
- ang -= 360.0f;
- }
- else if ( ang < -180.0f )
- {
- ang += 360.0f;
- }
-
- // calc side to side turning
- flYaw = ang - m_flGaitYaw;
- // Invert for mapping into 8way blend
- flYaw = -flYaw;
- flYaw = flYaw - (int)(flYaw / 360) * 360;
-
- if (flYaw < -180)
- {
- flYaw = flYaw + 360;
- }
- else if (flYaw > 180)
- {
- flYaw = flYaw - 360;
- }
-
- GetOuter()->SetPoseParameter( iYaw, flYaw );
-
-#ifndef CLIENT_DLL
- //Adrian: Make the model's angle match the legs so the hitboxes match on both sides.
- GetOuter()->SetLocalAngles( QAngle( GetOuter()->GetAnimEyeAngles().x, m_flCurrentFeetYaw, 0 ) );
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CPlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr )
-{
- // Get pitch from v_angle
- float flPitch = GetOuter()->GetLocalAngles()[ PITCH ];
-
- if ( flPitch > 180.0f )
- {
- flPitch -= 360.0f;
- }
- flPitch = clamp( flPitch, -90, 90 );
-
- QAngle absangles = GetOuter()->GetAbsAngles();
- absangles.x = 0.0f;
- m_angRender = absangles;
- m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f;
-
- // See if we have a blender for pitch
- GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : goal -
-// maxrate -
-// dt -
-// current -
-// Output : int
-//-----------------------------------------------------------------------------
-int CPlayerAnimState::ConvergeAngles( float goal,float maxrate, float dt, float& current )
-{
- int direction = TURN_NONE;
-
- float anglediff = goal - current;
- float anglediffabs = fabs( anglediff );
-
- anglediff = AngleNormalize( anglediff );
-
- float scale = 1.0f;
- if ( anglediffabs <= FADE_TURN_DEGREES )
- {
- scale = anglediffabs / FADE_TURN_DEGREES;
- // Always do at least a bit of the turn ( 1% )
- scale = clamp( scale, 0.01f, 1.0f );
- }
-
- float maxmove = maxrate * dt * scale;
-
- if ( fabs( anglediff ) < maxmove )
- {
- current = goal;
- }
- else
- {
- if ( anglediff > 0 )
- {
- current += maxmove;
- direction = TURN_LEFT;
- }
- else
- {
- current -= maxmove;
- direction = TURN_RIGHT;
- }
- }
-
- current = AngleNormalize( current );
-
- return direction;
-}
-
-void CPlayerAnimState::ComputePoseParam_BodyLookYaw( void )
-{
- QAngle absangles = GetOuter()->GetAbsAngles();
- absangles.y = AngleNormalize( absangles.y );
- m_angRender = absangles;
- m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f;
-
- // See if we even have a blender for pitch
- int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" );
- if ( upper_body_yaw < 0 )
- {
- return;
- }
-
- // Assume upper and lower bodies are aligned and that we're not turning
- float flGoalTorsoYaw = 0.0f;
- int turning = TURN_NONE;
- float turnrate = 360.0f;
-
- Vector vel;
-
- GetOuterAbsVelocity( vel );
-
- bool isMoving = ( vel.Length() > 1.0f ) ? true : false;
-
- if ( !isMoving )
- {
- // Just stopped moving, try and clamp feet
- if ( m_flLastTurnTime <= 0.0f )
- {
- m_flLastTurnTime = gpGlobals->curtime;
- m_flLastYaw = GetOuter()->GetAnimEyeAngles().y;
- // Snap feet to be perfectly aligned with torso/eyes
- m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y;
- m_flCurrentFeetYaw = m_flGoalFeetYaw;
- m_nTurningInPlace = TURN_NONE;
- }
-
- // If rotating in place, update stasis timer
- if ( m_flLastYaw != GetOuter()->GetAnimEyeAngles().y )
- {
- m_flLastTurnTime = gpGlobals->curtime;
- m_flLastYaw = GetOuter()->GetAnimEyeAngles().y;
- }
-
- if ( m_flGoalFeetYaw != m_flCurrentFeetYaw )
- {
- m_flLastTurnTime = gpGlobals->curtime;
- }
-
- turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
-
- QAngle eyeAngles = GetOuter()->GetAnimEyeAngles();
- QAngle vAngle = GetOuter()->GetLocalAngles();
-
- // See how far off current feetyaw is from true yaw
- float yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;
- yawdelta = AngleNormalize( yawdelta );
-
- bool rotated_too_far = false;
-
- float yawmagnitude = fabs( yawdelta );
-
- // If too far, then need to turn in place
- if ( yawmagnitude > 45 )
- {
- rotated_too_far = true;
- }
-
- // Standing still for a while, rotate feet around to face forward
- // Or rotated too far
- // FIXME: Play an in place turning animation
- if ( rotated_too_far ||
- ( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) )
- {
- m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y;
- m_flLastTurnTime = gpGlobals->curtime;
-
- /* float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw;
- if ( yd > 0 )
- {
- m_nTurningInPlace = TURN_RIGHT;
- }
- else if ( yd < 0 )
- {
- m_nTurningInPlace = TURN_LEFT;
- }
- else
- {
- m_nTurningInPlace = TURN_NONE;
- }
-
- turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
- yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;*/
-
- }
-
- // Snap upper body into position since the delta is already smoothed for the feet
- flGoalTorsoYaw = yawdelta;
- m_flCurrentTorsoYaw = flGoalTorsoYaw;
- }
- else
- {
- m_flLastTurnTime = 0.0f;
- m_nTurningInPlace = TURN_NONE;
- m_flCurrentFeetYaw = m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y;
- flGoalTorsoYaw = 0.0f;
- m_flCurrentTorsoYaw = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;
- }
-
-
- if ( turning == TURN_NONE )
- {
- m_nTurningInPlace = turning;
- }
-
- if ( m_nTurningInPlace != TURN_NONE )
- {
- // If we're close to finishing the turn, then turn off the turning animation
- if ( fabs( m_flCurrentFeetYaw - m_flGoalFeetYaw ) < MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION )
- {
- m_nTurningInPlace = TURN_NONE;
- }
- }
-
- // Rotate entire body into position
- absangles = GetOuter()->GetAbsAngles();
- absangles.y = m_flCurrentFeetYaw;
- m_angRender = absangles;
- m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f;
-
- GetOuter()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) );
-
- /*
- // FIXME: Adrian, what is this?
- int body_yaw = GetOuter()->LookupPoseParameter( "body_yaw" );
-
- if ( body_yaw >= 0 )
- {
- GetOuter()->SetPoseParameter( body_yaw, 30 );
- }
- */
-
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : activity -
-// Output : Activity
-//-----------------------------------------------------------------------------
-Activity CPlayerAnimState::BodyYawTranslateActivity( Activity activity )
-{
- // Not even standing still, sigh
- if ( activity != ACT_IDLE )
- return activity;
-
- // Not turning
- switch ( m_nTurningInPlace )
- {
- default:
- case TURN_NONE:
- return activity;
- /*
- case TURN_RIGHT:
- return ACT_TURNRIGHT45;
- case TURN_LEFT:
- return ACT_TURNLEFT45;
- */
- case TURN_RIGHT:
- case TURN_LEFT:
- return mp_ik.GetBool() ? ACT_TURN : activity;
- }
-
- Assert( 0 );
- return activity;
-}
-
-const QAngle& CPlayerAnimState::GetRenderAngles()
-{
- return m_angRender;
-}
-
-
-void CPlayerAnimState::GetOuterAbsVelocity( Vector& vel )
-{
-#if defined( CLIENT_DLL )
- GetOuter()->EstimateAbsVelocity( vel );
-#else
- vel = GetOuter()->GetAbsVelocity();
-#endif
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+
+#ifdef CLIENT_DLL
+#include "c_hl2mp_player.h"
+#include "prediction.h"
+#define CRecipientFilter C_RecipientFilter
+#else
+#include "hl2mp_player.h"
+#endif
+
+#include "engine/IEngineSound.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+
+extern ConVar sv_footsteps;
+
+const char *g_ppszPlayerSoundPrefixNames[PLAYER_SOUNDS_MAX] =
+{
+ "NPC_Citizen",
+ "NPC_CombineS",
+ "NPC_MetroPolice",
+};
+
+const char *CHL2MP_Player::GetPlayerModelSoundPrefix( void )
+{
+ return g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType];
+}
+
+void CHL2MP_Player::PrecacheFootStepSounds( void )
+{
+ int iFootstepSounds = ARRAYSIZE( g_ppszPlayerSoundPrefixNames );
+ int i;
+
+ for ( i = 0; i < iFootstepSounds; ++i )
+ {
+ char szFootStepName[128];
+
+ Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[i] );
+ PrecacheScriptSound( szFootStepName );
+
+ Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[i] );
+ PrecacheScriptSound( szFootStepName );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Consider the weapon's built-in accuracy, this character's proficiency with
+// the weapon, and the status of the target. Use this information to determine
+// how accurately to shoot at the target.
+//-----------------------------------------------------------------------------
+Vector CHL2MP_Player::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget )
+{
+ if ( pWeapon )
+ return pWeapon->GetBulletSpread( WEAPON_PROFICIENCY_PERFECT );
+
+ return VECTOR_CONE_15DEGREES;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : step -
+// fvol -
+// force - force sound to play
+//-----------------------------------------------------------------------------
+void CHL2MP_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force )
+{
+ if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
+ return;
+
+#if defined( CLIENT_DLL )
+ // during prediction play footstep sounds only once
+ if ( !prediction->IsFirstTimePredicted() )
+ return;
+#endif
+
+ if ( GetFlags() & FL_DUCKING )
+ return;
+
+ m_Local.m_nStepside = !m_Local.m_nStepside;
+
+ char szStepSound[128];
+
+ if ( m_Local.m_nStepside )
+ {
+ Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] );
+ }
+ else
+ {
+ Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] );
+ }
+
+ CSoundParameters params;
+ if ( GetParametersForSound( szStepSound, params, NULL ) == false )
+ return;
+
+ CRecipientFilter filter;
+ filter.AddRecipientsByPAS( vecOrigin );
+
+#ifndef CLIENT_DLL
+ // im MP, server removed all players in origins PVS, these players
+ // generate the footsteps clientside
+ if ( gpGlobals->maxClients > 1 )
+ filter.RemoveRecipientsByPVS( vecOrigin );
+#endif
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_BODY;
+ ep.m_pSoundName = params.soundname;
+ ep.m_flVolume = fvol;
+ ep.m_SoundLevel = params.soundlevel;
+ ep.m_nFlags = 0;
+ ep.m_nPitch = params.pitch;
+ ep.m_pOrigin = &vecOrigin;
+
+ EmitSound( filter, entindex(), ep );
+}
+
+
+//==========================
+// ANIMATION CODE
+//==========================
+
+
+// Below this many degrees, slow down turning rate linearly
+#define FADE_TURN_DEGREES 45.0f
+// After this, need to start turning feet
+#define MAX_TORSO_ANGLE 90.0f
+// Below this amount, don't play a turning animation/perform IK
+#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f
+
+static ConVar tf2_feetyawrunscale( "tf2_feetyawrunscale", "2", FCVAR_REPLICATED, "Multiplier on tf2_feetyawrate to allow turning faster when running." );
+extern ConVar sv_backspeed;
+extern ConVar mp_feetyawrate;
+extern ConVar mp_facefronttime;
+extern ConVar mp_ik;
+
+CPlayerAnimState::CPlayerAnimState( CHL2MP_Player *outer )
+ : m_pOuter( outer )
+{
+ m_flGaitYaw = 0.0f;
+ m_flGoalFeetYaw = 0.0f;
+ m_flCurrentFeetYaw = 0.0f;
+ m_flCurrentTorsoYaw = 0.0f;
+ m_flLastYaw = 0.0f;
+ m_flLastTurnTime = 0.0f;
+ m_flTurnCorrectionTime = 0.0f;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerAnimState::Update()
+{
+ m_angRender = GetOuter()->GetLocalAngles();
+ m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f;
+
+ ComputePoseParam_BodyYaw();
+ ComputePoseParam_BodyPitch(GetOuter()->GetModelPtr());
+ ComputePoseParam_BodyLookYaw();
+
+ ComputePlaybackRate();
+
+#ifdef CLIENT_DLL
+ GetOuter()->UpdateLookAt();
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerAnimState::ComputePlaybackRate()
+{
+ // Determine ideal playback rate
+ Vector vel;
+ GetOuterAbsVelocity( vel );
+
+ float speed = vel.Length2D();
+
+ bool isMoving = ( speed > 0.5f ) ? true : false;
+
+ float maxspeed = GetOuter()->GetSequenceGroundSpeed( GetOuter()->GetSequence() );
+
+ if ( isMoving && ( maxspeed > 0.0f ) )
+ {
+ float flFactor = 1.0f;
+
+ // Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below
+ GetOuter()->SetPlaybackRate( ( speed * flFactor ) / maxspeed );
+
+ // BUG BUG:
+ // This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed
+ }
+ else
+ {
+ GetOuter()->SetPlaybackRate( 1.0f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CBasePlayer
+//-----------------------------------------------------------------------------
+CHL2MP_Player *CPlayerAnimState::GetOuter()
+{
+ return m_pOuter;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : dt -
+//-----------------------------------------------------------------------------
+void CPlayerAnimState::EstimateYaw( void )
+{
+ float dt = gpGlobals->frametime;
+
+ if ( !dt )
+ {
+ return;
+ }
+
+ Vector est_velocity;
+ QAngle angles;
+
+ GetOuterAbsVelocity( est_velocity );
+
+ angles = GetOuter()->GetLocalAngles();
+
+ if ( est_velocity[1] == 0 && est_velocity[0] == 0 )
+ {
+ float flYawDiff = angles[YAW] - m_flGaitYaw;
+ flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360;
+ if (flYawDiff > 180)
+ flYawDiff -= 360;
+ if (flYawDiff < -180)
+ flYawDiff += 360;
+
+ if (dt < 0.25)
+ flYawDiff *= dt * 4;
+ else
+ flYawDiff *= dt;
+
+ m_flGaitYaw += flYawDiff;
+ m_flGaitYaw = m_flGaitYaw - (int)(m_flGaitYaw / 360) * 360;
+ }
+ else
+ {
+ m_flGaitYaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI);
+
+ if (m_flGaitYaw > 180)
+ m_flGaitYaw = 180;
+ else if (m_flGaitYaw < -180)
+ m_flGaitYaw = -180;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override for backpeddling
+// Input : dt -
+//-----------------------------------------------------------------------------
+void CPlayerAnimState::ComputePoseParam_BodyYaw( void )
+{
+ int iYaw = GetOuter()->LookupPoseParameter( "move_yaw" );
+ if ( iYaw < 0 )
+ return;
+
+ // view direction relative to movement
+ float flYaw;
+
+ EstimateYaw();
+
+ QAngle angles = GetOuter()->GetLocalAngles();
+ float ang = angles[ YAW ];
+ if ( ang > 180.0f )
+ {
+ ang -= 360.0f;
+ }
+ else if ( ang < -180.0f )
+ {
+ ang += 360.0f;
+ }
+
+ // calc side to side turning
+ flYaw = ang - m_flGaitYaw;
+ // Invert for mapping into 8way blend
+ flYaw = -flYaw;
+ flYaw = flYaw - (int)(flYaw / 360) * 360;
+
+ if (flYaw < -180)
+ {
+ flYaw = flYaw + 360;
+ }
+ else if (flYaw > 180)
+ {
+ flYaw = flYaw - 360;
+ }
+
+ GetOuter()->SetPoseParameter( iYaw, flYaw );
+
+#ifndef CLIENT_DLL
+ //Adrian: Make the model's angle match the legs so the hitboxes match on both sides.
+ GetOuter()->SetLocalAngles( QAngle( GetOuter()->GetAnimEyeAngles().x, m_flCurrentFeetYaw, 0 ) );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr )
+{
+ // Get pitch from v_angle
+ float flPitch = GetOuter()->GetLocalAngles()[ PITCH ];
+
+ if ( flPitch > 180.0f )
+ {
+ flPitch -= 360.0f;
+ }
+ flPitch = clamp( flPitch, -90, 90 );
+
+ QAngle absangles = GetOuter()->GetAbsAngles();
+ absangles.x = 0.0f;
+ m_angRender = absangles;
+ m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f;
+
+ // See if we have a blender for pitch
+ GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : goal -
+// maxrate -
+// dt -
+// current -
+// Output : int
+//-----------------------------------------------------------------------------
+int CPlayerAnimState::ConvergeAngles( float goal,float maxrate, float dt, float& current )
+{
+ int direction = TURN_NONE;
+
+ float anglediff = goal - current;
+ float anglediffabs = fabs( anglediff );
+
+ anglediff = AngleNormalize( anglediff );
+
+ float scale = 1.0f;
+ if ( anglediffabs <= FADE_TURN_DEGREES )
+ {
+ scale = anglediffabs / FADE_TURN_DEGREES;
+ // Always do at least a bit of the turn ( 1% )
+ scale = clamp( scale, 0.01f, 1.0f );
+ }
+
+ float maxmove = maxrate * dt * scale;
+
+ if ( fabs( anglediff ) < maxmove )
+ {
+ current = goal;
+ }
+ else
+ {
+ if ( anglediff > 0 )
+ {
+ current += maxmove;
+ direction = TURN_LEFT;
+ }
+ else
+ {
+ current -= maxmove;
+ direction = TURN_RIGHT;
+ }
+ }
+
+ current = AngleNormalize( current );
+
+ return direction;
+}
+
+void CPlayerAnimState::ComputePoseParam_BodyLookYaw( void )
+{
+ QAngle absangles = GetOuter()->GetAbsAngles();
+ absangles.y = AngleNormalize( absangles.y );
+ m_angRender = absangles;
+ m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f;
+
+ // See if we even have a blender for pitch
+ int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" );
+ if ( upper_body_yaw < 0 )
+ {
+ return;
+ }
+
+ // Assume upper and lower bodies are aligned and that we're not turning
+ float flGoalTorsoYaw = 0.0f;
+ int turning = TURN_NONE;
+ float turnrate = 360.0f;
+
+ Vector vel;
+
+ GetOuterAbsVelocity( vel );
+
+ bool isMoving = ( vel.Length() > 1.0f ) ? true : false;
+
+ if ( !isMoving )
+ {
+ // Just stopped moving, try and clamp feet
+ if ( m_flLastTurnTime <= 0.0f )
+ {
+ m_flLastTurnTime = gpGlobals->curtime;
+ m_flLastYaw = GetOuter()->GetAnimEyeAngles().y;
+ // Snap feet to be perfectly aligned with torso/eyes
+ m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y;
+ m_flCurrentFeetYaw = m_flGoalFeetYaw;
+ m_nTurningInPlace = TURN_NONE;
+ }
+
+ // If rotating in place, update stasis timer
+ if ( m_flLastYaw != GetOuter()->GetAnimEyeAngles().y )
+ {
+ m_flLastTurnTime = gpGlobals->curtime;
+ m_flLastYaw = GetOuter()->GetAnimEyeAngles().y;
+ }
+
+ if ( m_flGoalFeetYaw != m_flCurrentFeetYaw )
+ {
+ m_flLastTurnTime = gpGlobals->curtime;
+ }
+
+ turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
+
+ QAngle eyeAngles = GetOuter()->GetAnimEyeAngles();
+ QAngle vAngle = GetOuter()->GetLocalAngles();
+
+ // See how far off current feetyaw is from true yaw
+ float yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;
+ yawdelta = AngleNormalize( yawdelta );
+
+ bool rotated_too_far = false;
+
+ float yawmagnitude = fabs( yawdelta );
+
+ // If too far, then need to turn in place
+ if ( yawmagnitude > 45 )
+ {
+ rotated_too_far = true;
+ }
+
+ // Standing still for a while, rotate feet around to face forward
+ // Or rotated too far
+ // FIXME: Play an in place turning animation
+ if ( rotated_too_far ||
+ ( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) )
+ {
+ m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y;
+ m_flLastTurnTime = gpGlobals->curtime;
+
+ /* float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw;
+ if ( yd > 0 )
+ {
+ m_nTurningInPlace = TURN_RIGHT;
+ }
+ else if ( yd < 0 )
+ {
+ m_nTurningInPlace = TURN_LEFT;
+ }
+ else
+ {
+ m_nTurningInPlace = TURN_NONE;
+ }
+
+ turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
+ yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;*/
+
+ }
+
+ // Snap upper body into position since the delta is already smoothed for the feet
+ flGoalTorsoYaw = yawdelta;
+ m_flCurrentTorsoYaw = flGoalTorsoYaw;
+ }
+ else
+ {
+ m_flLastTurnTime = 0.0f;
+ m_nTurningInPlace = TURN_NONE;
+ m_flCurrentFeetYaw = m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y;
+ flGoalTorsoYaw = 0.0f;
+ m_flCurrentTorsoYaw = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;
+ }
+
+
+ if ( turning == TURN_NONE )
+ {
+ m_nTurningInPlace = turning;
+ }
+
+ if ( m_nTurningInPlace != TURN_NONE )
+ {
+ // If we're close to finishing the turn, then turn off the turning animation
+ if ( fabs( m_flCurrentFeetYaw - m_flGoalFeetYaw ) < MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION )
+ {
+ m_nTurningInPlace = TURN_NONE;
+ }
+ }
+
+ // Rotate entire body into position
+ absangles = GetOuter()->GetAbsAngles();
+ absangles.y = m_flCurrentFeetYaw;
+ m_angRender = absangles;
+ m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f;
+
+ GetOuter()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) );
+
+ /*
+ // FIXME: Adrian, what is this?
+ int body_yaw = GetOuter()->LookupPoseParameter( "body_yaw" );
+
+ if ( body_yaw >= 0 )
+ {
+ GetOuter()->SetPoseParameter( body_yaw, 30 );
+ }
+ */
+
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : activity -
+// Output : Activity
+//-----------------------------------------------------------------------------
+Activity CPlayerAnimState::BodyYawTranslateActivity( Activity activity )
+{
+ // Not even standing still, sigh
+ if ( activity != ACT_IDLE )
+ return activity;
+
+ // Not turning
+ switch ( m_nTurningInPlace )
+ {
+ default:
+ case TURN_NONE:
+ return activity;
+ /*
+ case TURN_RIGHT:
+ return ACT_TURNRIGHT45;
+ case TURN_LEFT:
+ return ACT_TURNLEFT45;
+ */
+ case TURN_RIGHT:
+ case TURN_LEFT:
+ return mp_ik.GetBool() ? ACT_TURN : activity;
+ }
+
+ Assert( 0 );
+ return activity;
+}
+
+const QAngle& CPlayerAnimState::GetRenderAngles()
+{
+ return m_angRender;
+}
+
+
+void CPlayerAnimState::GetOuterAbsVelocity( Vector& vel )
+{
+#if defined( CLIENT_DLL )
+ GetOuter()->EstimateAbsVelocity( vel );
+#else
+ vel = GetOuter()->GetAbsVelocity();
+#endif
} \ No newline at end of file
diff --git a/mp/src/game/shared/hl2mp/hl2mp_player_shared.h b/mp/src/game/shared/hl2mp/hl2mp_player_shared.h
index daaaebdf..3aee9237 100644
--- a/mp/src/game/shared/hl2mp/hl2mp_player_shared.h
+++ b/mp/src/game/shared/hl2mp/hl2mp_player_shared.h
@@ -1,97 +1,97 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-#ifndef HL2MP_PLAYER_SHARED_H
-#define HL2MP_PLAYER_SHARED_H
-#pragma once
-
-#define HL2MP_PUSHAWAY_THINK_INTERVAL (1.0f / 20.0f)
-#include "studio.h"
-
-
-enum
-{
- PLAYER_SOUNDS_CITIZEN = 0,
- PLAYER_SOUNDS_COMBINESOLDIER,
- PLAYER_SOUNDS_METROPOLICE,
- PLAYER_SOUNDS_MAX,
-};
-
-enum HL2MPPlayerState
-{
- // Happily running around in the game.
- STATE_ACTIVE=0,
- STATE_OBSERVER_MODE, // Noclipping around, watching players, etc.
- NUM_PLAYER_STATES
-};
-
-
-#if defined( CLIENT_DLL )
-#define CHL2MP_Player C_HL2MP_Player
-#endif
-
-class CPlayerAnimState
-{
-public:
- enum
- {
- TURN_NONE = 0,
- TURN_LEFT,
- TURN_RIGHT
- };
-
- CPlayerAnimState( CHL2MP_Player *outer );
-
- Activity BodyYawTranslateActivity( Activity activity );
-
- void Update();
-
- const QAngle& GetRenderAngles();
-
- void GetPoseParameters( CStudioHdr *pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM] );
-
- CHL2MP_Player *GetOuter();
-
-private:
- void GetOuterAbsVelocity( Vector& vel );
-
- int ConvergeAngles( float goal,float maxrate, float dt, float& current );
-
- void EstimateYaw( void );
- void ComputePoseParam_BodyYaw( void );
- void ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr );
- void ComputePoseParam_BodyLookYaw( void );
-
- void ComputePlaybackRate();
-
- CHL2MP_Player *m_pOuter;
-
- float m_flGaitYaw;
- float m_flStoredCycle;
-
- // The following variables are used for tweaking the yaw of the upper body when standing still and
- // making sure that it smoothly blends in and out once the player starts moving
- // Direction feet were facing when we stopped moving
- float m_flGoalFeetYaw;
- float m_flCurrentFeetYaw;
-
- float m_flCurrentTorsoYaw;
-
- // To check if they are rotating in place
- float m_flLastYaw;
- // Time when we stopped moving
- float m_flLastTurnTime;
-
- // One of the above enums
- int m_nTurningInPlace;
-
- QAngle m_angRender;
-
- float m_flTurnCorrectionTime;
-};
-
-#endif //HL2MP_PLAYER_SHARED_h
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#ifndef HL2MP_PLAYER_SHARED_H
+#define HL2MP_PLAYER_SHARED_H
+#pragma once
+
+#define HL2MP_PUSHAWAY_THINK_INTERVAL (1.0f / 20.0f)
+#include "studio.h"
+
+
+enum
+{
+ PLAYER_SOUNDS_CITIZEN = 0,
+ PLAYER_SOUNDS_COMBINESOLDIER,
+ PLAYER_SOUNDS_METROPOLICE,
+ PLAYER_SOUNDS_MAX,
+};
+
+enum HL2MPPlayerState
+{
+ // Happily running around in the game.
+ STATE_ACTIVE=0,
+ STATE_OBSERVER_MODE, // Noclipping around, watching players, etc.
+ NUM_PLAYER_STATES
+};
+
+
+#if defined( CLIENT_DLL )
+#define CHL2MP_Player C_HL2MP_Player
+#endif
+
+class CPlayerAnimState
+{
+public:
+ enum
+ {
+ TURN_NONE = 0,
+ TURN_LEFT,
+ TURN_RIGHT
+ };
+
+ CPlayerAnimState( CHL2MP_Player *outer );
+
+ Activity BodyYawTranslateActivity( Activity activity );
+
+ void Update();
+
+ const QAngle& GetRenderAngles();
+
+ void GetPoseParameters( CStudioHdr *pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM] );
+
+ CHL2MP_Player *GetOuter();
+
+private:
+ void GetOuterAbsVelocity( Vector& vel );
+
+ int ConvergeAngles( float goal,float maxrate, float dt, float& current );
+
+ void EstimateYaw( void );
+ void ComputePoseParam_BodyYaw( void );
+ void ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr );
+ void ComputePoseParam_BodyLookYaw( void );
+
+ void ComputePlaybackRate();
+
+ CHL2MP_Player *m_pOuter;
+
+ float m_flGaitYaw;
+ float m_flStoredCycle;
+
+ // The following variables are used for tweaking the yaw of the upper body when standing still and
+ // making sure that it smoothly blends in and out once the player starts moving
+ // Direction feet were facing when we stopped moving
+ float m_flGoalFeetYaw;
+ float m_flCurrentFeetYaw;
+
+ float m_flCurrentTorsoYaw;
+
+ // To check if they are rotating in place
+ float m_flLastYaw;
+ // Time when we stopped moving
+ float m_flLastTurnTime;
+
+ // One of the above enums
+ int m_nTurningInPlace;
+
+ QAngle m_angRender;
+
+ float m_flTurnCorrectionTime;
+};
+
+#endif //HL2MP_PLAYER_SHARED_h
diff --git a/mp/src/game/shared/hl2mp/hl2mp_weapon_parse.cpp b/mp/src/game/shared/hl2mp/hl2mp_weapon_parse.cpp
index 6b21d8df..9b83dce7 100644
--- a/mp/src/game/shared/hl2mp/hl2mp_weapon_parse.cpp
+++ b/mp/src/game/shared/hl2mp/hl2mp_weapon_parse.cpp
@@ -1,32 +1,32 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include <KeyValues.h>
-#include "hl2mp_weapon_parse.h"
-#include "ammodef.h"
-
-FileWeaponInfo_t* CreateWeaponInfo()
-{
- return new CHL2MPSWeaponInfo;
-}
-
-
-
-CHL2MPSWeaponInfo::CHL2MPSWeaponInfo()
-{
- m_iPlayerDamage = 0;
-}
-
-
-void CHL2MPSWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
-{
- BaseClass::Parse( pKeyValuesData, szWeaponName );
-
- m_iPlayerDamage = pKeyValuesData->GetInt( "damage", 0 );
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include <KeyValues.h>
+#include "hl2mp_weapon_parse.h"
+#include "ammodef.h"
+
+FileWeaponInfo_t* CreateWeaponInfo()
+{
+ return new CHL2MPSWeaponInfo;
+}
+
+
+
+CHL2MPSWeaponInfo::CHL2MPSWeaponInfo()
+{
+ m_iPlayerDamage = 0;
+}
+
+
+void CHL2MPSWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
+{
+ BaseClass::Parse( pKeyValuesData, szWeaponName );
+
+ m_iPlayerDamage = pKeyValuesData->GetInt( "damage", 0 );
+}
+
+
diff --git a/mp/src/game/shared/hl2mp/hl2mp_weapon_parse.h b/mp/src/game/shared/hl2mp/hl2mp_weapon_parse.h
index 211c2c9c..2ec16dd6 100644
--- a/mp/src/game/shared/hl2mp/hl2mp_weapon_parse.h
+++ b/mp/src/game/shared/hl2mp/hl2mp_weapon_parse.h
@@ -1,35 +1,35 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef HL2MP_WEAPON_PARSE_H
-#define HL2MP_WEAPON_PARSE_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#include "weapon_parse.h"
-#include "networkvar.h"
-
-
-//--------------------------------------------------------------------------------------------------------
-class CHL2MPSWeaponInfo : public FileWeaponInfo_t
-{
-public:
- DECLARE_CLASS_GAMEROOT( CHL2MPSWeaponInfo, FileWeaponInfo_t );
-
- CHL2MPSWeaponInfo();
-
- virtual void Parse( ::KeyValues *pKeyValuesData, const char *szWeaponName );
-
-
-public:
-
- int m_iPlayerDamage;
-};
-
-
-#endif // HL2MP_WEAPON_PARSE_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef HL2MP_WEAPON_PARSE_H
+#define HL2MP_WEAPON_PARSE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_parse.h"
+#include "networkvar.h"
+
+
+//--------------------------------------------------------------------------------------------------------
+class CHL2MPSWeaponInfo : public FileWeaponInfo_t
+{
+public:
+ DECLARE_CLASS_GAMEROOT( CHL2MPSWeaponInfo, FileWeaponInfo_t );
+
+ CHL2MPSWeaponInfo();
+
+ virtual void Parse( ::KeyValues *pKeyValuesData, const char *szWeaponName );
+
+
+public:
+
+ int m_iPlayerDamage;
+};
+
+
+#endif // HL2MP_WEAPON_PARSE_H
diff --git a/mp/src/game/shared/hl2mp/weapon_357.cpp b/mp/src/game/shared/hl2mp/weapon_357.cpp
index f5d444d5..d943f278 100644
--- a/mp/src/game/shared/hl2mp/weapon_357.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_357.cpp
@@ -1,154 +1,154 @@
-
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "in_buttons.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
-#else
- #include "hl2mp_player.h"
-#endif
-
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-
-#ifdef CLIENT_DLL
-#define CWeapon357 C_Weapon357
-#endif
-
-//-----------------------------------------------------------------------------
-// CWeapon357
-//-----------------------------------------------------------------------------
-
-class CWeapon357 : public CBaseHL2MPCombatWeapon
-{
- DECLARE_CLASS( CWeapon357, CBaseHL2MPCombatWeapon );
-public:
-
- CWeapon357( void );
-
- void PrimaryAttack( void );
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-
-private:
-
- CWeapon357( const CWeapon357 & );
-};
-
-IMPLEMENT_NETWORKCLASS_ALIASED( Weapon357, DT_Weapon357 )
-
-BEGIN_NETWORK_TABLE( CWeapon357, DT_Weapon357 )
-END_NETWORK_TABLE()
-
-BEGIN_PREDICTION_DATA( CWeapon357 )
-END_PREDICTION_DATA()
-
-LINK_ENTITY_TO_CLASS( weapon_357, CWeapon357 );
-PRECACHE_WEAPON_REGISTER( weapon_357 );
-
-
-#ifndef CLIENT_DLL
-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 },
-};
-
-
-
-IMPLEMENT_ACTTABLE( CWeapon357 );
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-//-----------------------------------------------------------------------------
-CWeapon357::CWeapon357( void )
-{
- m_bReloadsSingly = false;
- m_bFiresUnderwater = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeapon357::PrimaryAttack( void )
-{
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if ( !pPlayer )
- {
- return;
- }
-
- if ( m_iClip1 <= 0 )
- {
- if ( !m_bFireOnEmpty )
- {
- Reload();
- }
- else
- {
- WeaponSound( EMPTY );
- m_flNextPrimaryAttack = 0.15;
- }
-
- return;
- }
-
- WeaponSound( SINGLE );
- pPlayer->DoMuzzleFlash();
-
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.75;
- m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
-
- m_iClip1--;
-
- Vector vecSrc = pPlayer->Weapon_ShootPosition();
- Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
-
- FireBulletsInfo_t info( 1, vecSrc, vecAiming, vec3_origin, MAX_TRACE_LENGTH, m_iPrimaryAmmoType );
- info.m_pAttacker = pPlayer;
-
- // Fire the bullets, and force the first shot to be perfectly accuracy
- pPlayer->FireBullets( info );
-
- //Disorient the player
- QAngle angles = pPlayer->GetLocalAngles();
-
- angles.x += random->RandomInt( -1, 1 );
- angles.y += random->RandomInt( -1, 1 );
- angles.z = 0;
-
-#ifndef CLIENT_DLL
- pPlayer->SnapEyeAngles( angles );
-#endif
-
- pPlayer->ViewPunch( QAngle( -8, random->RandomFloat( -2, 2 ), 0 ) );
-
- if ( !m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
- {
- // HEV suit - indicate out of ammo condition
- pPlayer->SetSuitUpdate( "!HEV_AMO0", FALSE, 0 );
- }
-}
+
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+#else
+ #include "hl2mp_player.h"
+#endif
+
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+
+#ifdef CLIENT_DLL
+#define CWeapon357 C_Weapon357
+#endif
+
+//-----------------------------------------------------------------------------
+// CWeapon357
+//-----------------------------------------------------------------------------
+
+class CWeapon357 : public CBaseHL2MPCombatWeapon
+{
+ DECLARE_CLASS( CWeapon357, CBaseHL2MPCombatWeapon );
+public:
+
+ CWeapon357( void );
+
+ void PrimaryAttack( void );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+
+private:
+
+ CWeapon357( const CWeapon357 & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( Weapon357, DT_Weapon357 )
+
+BEGIN_NETWORK_TABLE( CWeapon357, DT_Weapon357 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeapon357 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_357, CWeapon357 );
+PRECACHE_WEAPON_REGISTER( weapon_357 );
+
+
+#ifndef CLIENT_DLL
+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 },
+};
+
+
+
+IMPLEMENT_ACTTABLE( CWeapon357 );
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CWeapon357::CWeapon357( void )
+{
+ m_bReloadsSingly = false;
+ m_bFiresUnderwater = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeapon357::PrimaryAttack( void )
+{
+ // Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if ( !pPlayer )
+ {
+ return;
+ }
+
+ if ( m_iClip1 <= 0 )
+ {
+ if ( !m_bFireOnEmpty )
+ {
+ Reload();
+ }
+ else
+ {
+ WeaponSound( EMPTY );
+ m_flNextPrimaryAttack = 0.15;
+ }
+
+ return;
+ }
+
+ WeaponSound( SINGLE );
+ pPlayer->DoMuzzleFlash();
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.75;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
+
+ m_iClip1--;
+
+ Vector vecSrc = pPlayer->Weapon_ShootPosition();
+ Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
+
+ FireBulletsInfo_t info( 1, vecSrc, vecAiming, vec3_origin, MAX_TRACE_LENGTH, m_iPrimaryAmmoType );
+ info.m_pAttacker = pPlayer;
+
+ // Fire the bullets, and force the first shot to be perfectly accuracy
+ pPlayer->FireBullets( info );
+
+ //Disorient the player
+ QAngle angles = pPlayer->GetLocalAngles();
+
+ angles.x += random->RandomInt( -1, 1 );
+ angles.y += random->RandomInt( -1, 1 );
+ angles.z = 0;
+
+#ifndef CLIENT_DLL
+ pPlayer->SnapEyeAngles( angles );
+#endif
+
+ pPlayer->ViewPunch( QAngle( -8, random->RandomFloat( -2, 2 ), 0 ) );
+
+ if ( !m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate( "!HEV_AMO0", FALSE, 0 );
+ }
+}
diff --git a/mp/src/game/shared/hl2mp/weapon_ar2.cpp b/mp/src/game/shared/hl2mp/weapon_ar2.cpp
index 90ed6a5b..5086db1b 100644
--- a/mp/src/game/shared/hl2mp/weapon_ar2.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_ar2.cpp
@@ -1,312 +1,312 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
- #include "c_te_effect_dispatch.h"
-#else
- #include "hl2mp_player.h"
- #include "te_effect_dispatch.h"
- #include "prop_combine_ball.h"
-#endif
-
-#include "weapon_ar2.h"
-#include "effect_dispatch_data.h"
-
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#ifndef CLIENT_DLL
-ConVar sk_weapon_ar2_alt_fire_radius( "sk_weapon_ar2_alt_fire_radius", "10" );
-ConVar sk_weapon_ar2_alt_fire_duration( "sk_weapon_ar2_alt_fire_duration", "4" );
-ConVar sk_weapon_ar2_alt_fire_mass( "sk_weapon_ar2_alt_fire_mass", "150" );
-#endif
-
-//=========================================================
-//=========================================================
-
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponAR2, DT_WeaponAR2 )
-
-BEGIN_NETWORK_TABLE( CWeaponAR2, DT_WeaponAR2 )
-END_NETWORK_TABLE()
-
-BEGIN_PREDICTION_DATA( CWeaponAR2 )
-END_PREDICTION_DATA()
-
-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 },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponAR2);
-
-#endif
-
-CWeaponAR2::CWeaponAR2( )
-{
- m_fMinRange1 = 65;
- m_fMaxRange1 = 2048;
-
- m_fMinRange2 = 256;
- m_fMaxRange2 = 1024;
-
- m_nShotsFired = 0;
- m_nVentPose = -1;
-}
-
-void CWeaponAR2::Precache( void )
-{
- BaseClass::Precache();
-
-#ifndef CLIENT_DLL
-
- UTIL_PrecacheOther( "prop_combine_ball" );
- UTIL_PrecacheOther( "env_entity_dissolver" );
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Handle grenade detonate in-air (even when no ammo is left)
-//-----------------------------------------------------------------------------
-void CWeaponAR2::ItemPostFrame( void )
-{
- // See if we need to fire off our secondary round
- if ( m_bShotDelayed && gpGlobals->curtime > m_flDelayedFire )
- {
- DelayedAttack();
- }
-
- // Update our pose parameter for the vents
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner )
- {
- CBaseViewModel *pVM = pOwner->GetViewModel();
-
- if ( pVM )
- {
- if ( m_nVentPose == -1 )
- {
- m_nVentPose = pVM->LookupPoseParameter( "VentPoses" );
- }
-
- float flVentPose = RemapValClamped( m_nShotsFired, 0, 5, 0.0f, 1.0f );
- pVM->SetPoseParameter( m_nVentPose, flVentPose );
- }
- }
-
- BaseClass::ItemPostFrame();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Activity
-//-----------------------------------------------------------------------------
-Activity CWeaponAR2::GetPrimaryAttackActivity( void )
-{
- if ( m_nShotsFired < 2 )
- return ACT_VM_PRIMARYATTACK;
-
- if ( m_nShotsFired < 3 )
- return ACT_VM_RECOIL1;
-
- if ( m_nShotsFired < 4 )
- return ACT_VM_RECOIL2;
-
- return ACT_VM_RECOIL3;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &tr -
-// nDamageType -
-//-----------------------------------------------------------------------------
-void CWeaponAR2::DoImpactEffect( trace_t &tr, int nDamageType )
-{
- CEffectData data;
-
- data.m_vOrigin = tr.endpos + ( tr.plane.normal * 1.0f );
- data.m_vNormal = tr.plane.normal;
-
- DispatchEffect( "AR2Impact", data );
-
- BaseClass::DoImpactEffect( tr, nDamageType );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponAR2::DelayedAttack( void )
-{
- m_bShotDelayed = false;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- // Deplete the clip completely
- SendWeaponAnim( ACT_VM_SECONDARYATTACK );
- m_flNextSecondaryAttack = pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration();
-
- // Register a muzzleflash for the AI
- pOwner->DoMuzzleFlash();
-
- WeaponSound( WPN_DOUBLE );
-
- // Fire the bullets
- Vector vecSrc = pOwner->Weapon_ShootPosition( );
- Vector vecAiming = pOwner->GetAutoaimVector( AUTOAIM_2DEGREES );
- Vector impactPoint = vecSrc + ( vecAiming * MAX_TRACE_LENGTH );
-
- // Fire the bullets
- Vector vecVelocity = vecAiming * 1000.0f;
-
-#ifndef CLIENT_DLL
- // Fire the combine ball
- CreateCombineBall( vecSrc,
- vecVelocity,
- sk_weapon_ar2_alt_fire_radius.GetFloat(),
- sk_weapon_ar2_alt_fire_mass.GetFloat(),
- sk_weapon_ar2_alt_fire_duration.GetFloat(),
- pOwner );
-
- // View effects
- color32 white = {255, 255, 255, 64};
- UTIL_ScreenFade( pOwner, white, 0.1, 0, FFADE_IN );
-#endif
-
- //Disorient the player
- QAngle angles = pOwner->GetLocalAngles();
-
- angles.x += random->RandomInt( -4, 4 );
- angles.y += random->RandomInt( -4, 4 );
- angles.z = 0;
-
-// pOwner->SnapEyeAngles( angles );
-
- pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -8, -12 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) );
-
- // Decrease ammo
- pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );
-
- // Can shoot again immediately
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
-
- // Can blow up after a short delay (so have time to release mouse button)
- m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponAR2::SecondaryAttack( void )
-{
- if ( m_bShotDelayed )
- return;
-
- // Cannot fire underwater
- if ( GetOwner() && GetOwner()->GetWaterLevel() == 3 )
- {
- SendWeaponAnim( ACT_VM_DRYFIRE );
- BaseClass::WeaponSound( EMPTY );
- m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
- return;
- }
-
- m_bShotDelayed = true;
- m_flNextPrimaryAttack = m_flNextSecondaryAttack = m_flDelayedFire = gpGlobals->curtime + 0.5f;
-
- SendWeaponAnim( ACT_VM_FIDGET );
- WeaponSound( SPECIAL1 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override if we're waiting to release a shot
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponAR2::CanHolster( void )
-{
- if ( m_bShotDelayed )
- return false;
-
- return BaseClass::CanHolster();
-}
-
-
-bool CWeaponAR2::Deploy( void )
-{
- m_bShotDelayed = false;
- m_flDelayedFire = 0.0f;
-
- return BaseClass::Deploy();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override if we're waiting to release a shot
-//-----------------------------------------------------------------------------
-bool CWeaponAR2::Reload( void )
-{
- if ( m_bShotDelayed )
- return false;
-
- return BaseClass::Reload();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponAR2::AddViewKick( void )
-{
- #define EASY_DAMPEN 0.5f
- #define MAX_VERTICAL_KICK 8.0f //Degrees
- #define SLIDE_LIMIT 5.0f //Seconds
-
- //Get the view kick
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if (!pPlayer)
- return;
-
- DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK, m_fFireDuration, SLIDE_LIMIT );
-}
-
-//-----------------------------------------------------------------------------
-const WeaponProficiencyInfo_t *CWeaponAR2::GetProficiencyValues()
-{
- static WeaponProficiencyInfo_t proficiencyTable[] =
- {
- { 7.0, 0.75 },
- { 5.00, 0.75 },
- { 3.0, 0.85 },
- { 5.0/3.0, 0.75 },
- { 1.00, 1.0 },
- };
-
- COMPILE_TIME_ASSERT( ARRAYSIZE(proficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1);
-
- return proficiencyTable;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+ #include "c_te_effect_dispatch.h"
+#else
+ #include "hl2mp_player.h"
+ #include "te_effect_dispatch.h"
+ #include "prop_combine_ball.h"
+#endif
+
+#include "weapon_ar2.h"
+#include "effect_dispatch_data.h"
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifndef CLIENT_DLL
+ConVar sk_weapon_ar2_alt_fire_radius( "sk_weapon_ar2_alt_fire_radius", "10" );
+ConVar sk_weapon_ar2_alt_fire_duration( "sk_weapon_ar2_alt_fire_duration", "4" );
+ConVar sk_weapon_ar2_alt_fire_mass( "sk_weapon_ar2_alt_fire_mass", "150" );
+#endif
+
+//=========================================================
+//=========================================================
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponAR2, DT_WeaponAR2 )
+
+BEGIN_NETWORK_TABLE( CWeaponAR2, DT_WeaponAR2 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponAR2 )
+END_PREDICTION_DATA()
+
+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 },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponAR2);
+
+#endif
+
+CWeaponAR2::CWeaponAR2( )
+{
+ m_fMinRange1 = 65;
+ m_fMaxRange1 = 2048;
+
+ m_fMinRange2 = 256;
+ m_fMaxRange2 = 1024;
+
+ m_nShotsFired = 0;
+ m_nVentPose = -1;
+}
+
+void CWeaponAR2::Precache( void )
+{
+ BaseClass::Precache();
+
+#ifndef CLIENT_DLL
+
+ UTIL_PrecacheOther( "prop_combine_ball" );
+ UTIL_PrecacheOther( "env_entity_dissolver" );
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle grenade detonate in-air (even when no ammo is left)
+//-----------------------------------------------------------------------------
+void CWeaponAR2::ItemPostFrame( void )
+{
+ // See if we need to fire off our secondary round
+ if ( m_bShotDelayed && gpGlobals->curtime > m_flDelayedFire )
+ {
+ DelayedAttack();
+ }
+
+ // Update our pose parameter for the vents
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner )
+ {
+ CBaseViewModel *pVM = pOwner->GetViewModel();
+
+ if ( pVM )
+ {
+ if ( m_nVentPose == -1 )
+ {
+ m_nVentPose = pVM->LookupPoseParameter( "VentPoses" );
+ }
+
+ float flVentPose = RemapValClamped( m_nShotsFired, 0, 5, 0.0f, 1.0f );
+ pVM->SetPoseParameter( m_nVentPose, flVentPose );
+ }
+ }
+
+ BaseClass::ItemPostFrame();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Activity
+//-----------------------------------------------------------------------------
+Activity CWeaponAR2::GetPrimaryAttackActivity( void )
+{
+ if ( m_nShotsFired < 2 )
+ return ACT_VM_PRIMARYATTACK;
+
+ if ( m_nShotsFired < 3 )
+ return ACT_VM_RECOIL1;
+
+ if ( m_nShotsFired < 4 )
+ return ACT_VM_RECOIL2;
+
+ return ACT_VM_RECOIL3;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &tr -
+// nDamageType -
+//-----------------------------------------------------------------------------
+void CWeaponAR2::DoImpactEffect( trace_t &tr, int nDamageType )
+{
+ CEffectData data;
+
+ data.m_vOrigin = tr.endpos + ( tr.plane.normal * 1.0f );
+ data.m_vNormal = tr.plane.normal;
+
+ DispatchEffect( "AR2Impact", data );
+
+ BaseClass::DoImpactEffect( tr, nDamageType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponAR2::DelayedAttack( void )
+{
+ m_bShotDelayed = false;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ // Deplete the clip completely
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+ m_flNextSecondaryAttack = pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration();
+
+ // Register a muzzleflash for the AI
+ pOwner->DoMuzzleFlash();
+
+ WeaponSound( WPN_DOUBLE );
+
+ // Fire the bullets
+ Vector vecSrc = pOwner->Weapon_ShootPosition( );
+ Vector vecAiming = pOwner->GetAutoaimVector( AUTOAIM_2DEGREES );
+ Vector impactPoint = vecSrc + ( vecAiming * MAX_TRACE_LENGTH );
+
+ // Fire the bullets
+ Vector vecVelocity = vecAiming * 1000.0f;
+
+#ifndef CLIENT_DLL
+ // Fire the combine ball
+ CreateCombineBall( vecSrc,
+ vecVelocity,
+ sk_weapon_ar2_alt_fire_radius.GetFloat(),
+ sk_weapon_ar2_alt_fire_mass.GetFloat(),
+ sk_weapon_ar2_alt_fire_duration.GetFloat(),
+ pOwner );
+
+ // View effects
+ color32 white = {255, 255, 255, 64};
+ UTIL_ScreenFade( pOwner, white, 0.1, 0, FFADE_IN );
+#endif
+
+ //Disorient the player
+ QAngle angles = pOwner->GetLocalAngles();
+
+ angles.x += random->RandomInt( -4, 4 );
+ angles.y += random->RandomInt( -4, 4 );
+ angles.z = 0;
+
+// pOwner->SnapEyeAngles( angles );
+
+ pOwner->ViewPunch( QAngle( SharedRandomInt( "ar2pax", -8, -12 ), SharedRandomInt( "ar2pay", 1, 2 ), 0 ) );
+
+ // Decrease ammo
+ pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );
+
+ // Can shoot again immediately
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
+
+ // Can blow up after a short delay (so have time to release mouse button)
+ m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponAR2::SecondaryAttack( void )
+{
+ if ( m_bShotDelayed )
+ return;
+
+ // Cannot fire underwater
+ if ( GetOwner() && GetOwner()->GetWaterLevel() == 3 )
+ {
+ SendWeaponAnim( ACT_VM_DRYFIRE );
+ BaseClass::WeaponSound( EMPTY );
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
+ return;
+ }
+
+ m_bShotDelayed = true;
+ m_flNextPrimaryAttack = m_flNextSecondaryAttack = m_flDelayedFire = gpGlobals->curtime + 0.5f;
+
+ SendWeaponAnim( ACT_VM_FIDGET );
+ WeaponSound( SPECIAL1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override if we're waiting to release a shot
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponAR2::CanHolster( void )
+{
+ if ( m_bShotDelayed )
+ return false;
+
+ return BaseClass::CanHolster();
+}
+
+
+bool CWeaponAR2::Deploy( void )
+{
+ m_bShotDelayed = false;
+ m_flDelayedFire = 0.0f;
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override if we're waiting to release a shot
+//-----------------------------------------------------------------------------
+bool CWeaponAR2::Reload( void )
+{
+ if ( m_bShotDelayed )
+ return false;
+
+ return BaseClass::Reload();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponAR2::AddViewKick( void )
+{
+ #define EASY_DAMPEN 0.5f
+ #define MAX_VERTICAL_KICK 8.0f //Degrees
+ #define SLIDE_LIMIT 5.0f //Seconds
+
+ //Get the view kick
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if (!pPlayer)
+ return;
+
+ DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK, m_fFireDuration, SLIDE_LIMIT );
+}
+
+//-----------------------------------------------------------------------------
+const WeaponProficiencyInfo_t *CWeaponAR2::GetProficiencyValues()
+{
+ static WeaponProficiencyInfo_t proficiencyTable[] =
+ {
+ { 7.0, 0.75 },
+ { 5.00, 0.75 },
+ { 3.0, 0.85 },
+ { 5.0/3.0, 0.75 },
+ { 1.00, 1.0 },
+ };
+
+ COMPILE_TIME_ASSERT( ARRAYSIZE(proficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1);
+
+ return proficiencyTable;
+}
diff --git a/mp/src/game/shared/hl2mp/weapon_ar2.h b/mp/src/game/shared/hl2mp/weapon_ar2.h
index f2222038..8aa8dde8 100644
--- a/mp/src/game/shared/hl2mp/weapon_ar2.h
+++ b/mp/src/game/shared/hl2mp/weapon_ar2.h
@@ -1,84 +1,84 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Projectile shot from the AR2
-//
-// $Workfile: $
-// $Date: $
-//
-//-----------------------------------------------------------------------------
-// $Log: $
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef WEAPONAR2_H
-#define WEAPONAR2_H
-
-#include "basegrenade_shared.h"
-#include "weapon_hl2mpbase_machinegun.h"
-
-#ifdef CLIENT_DLL
-#define CWeaponAR2 C_WeaponAR2
-#endif
-
-class CWeaponAR2 : public CHL2MPMachineGun
-{
-public:
- DECLARE_CLASS( CWeaponAR2, CHL2MPMachineGun );
-
- CWeaponAR2();
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- void ItemPostFrame( void );
- void Precache( void );
-
- void SecondaryAttack( void );
- void DelayedAttack( void );
-
- const char *GetTracerType( void ) { return "AR2Tracer"; }
-
- void AddViewKick( void );
-
- int GetMinBurst( void ) { return 2; }
- int GetMaxBurst( void ) { return 5; }
- float GetFireRate( void ) { return 0.1f; }
-
- bool CanHolster( void );
- bool Reload( void );
-
- Activity GetPrimaryAttackActivity( void );
-
- void DoImpactEffect( trace_t &tr, int nDamageType );
-
- virtual bool Deploy( void );
-
-
- virtual const Vector& GetBulletSpread( void )
- {
- static Vector cone;
-
- cone = VECTOR_CONE_3DEGREES;
-
- return cone;
- }
-
- const WeaponProficiencyInfo_t *GetProficiencyValues();
-
-private:
- CWeaponAR2( const CWeaponAR2 & );
-
-protected:
-
- float m_flDelayedFire;
- bool m_bShotDelayed;
- int m_nVentPose;
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-};
-
-
-#endif //WEAPONAR2_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Projectile shot from the AR2
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPONAR2_H
+#define WEAPONAR2_H
+
+#include "basegrenade_shared.h"
+#include "weapon_hl2mpbase_machinegun.h"
+
+#ifdef CLIENT_DLL
+#define CWeaponAR2 C_WeaponAR2
+#endif
+
+class CWeaponAR2 : public CHL2MPMachineGun
+{
+public:
+ DECLARE_CLASS( CWeaponAR2, CHL2MPMachineGun );
+
+ CWeaponAR2();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ void ItemPostFrame( void );
+ void Precache( void );
+
+ void SecondaryAttack( void );
+ void DelayedAttack( void );
+
+ const char *GetTracerType( void ) { return "AR2Tracer"; }
+
+ void AddViewKick( void );
+
+ int GetMinBurst( void ) { return 2; }
+ int GetMaxBurst( void ) { return 5; }
+ float GetFireRate( void ) { return 0.1f; }
+
+ bool CanHolster( void );
+ bool Reload( void );
+
+ Activity GetPrimaryAttackActivity( void );
+
+ void DoImpactEffect( trace_t &tr, int nDamageType );
+
+ virtual bool Deploy( void );
+
+
+ virtual const Vector& GetBulletSpread( void )
+ {
+ static Vector cone;
+
+ cone = VECTOR_CONE_3DEGREES;
+
+ return cone;
+ }
+
+ const WeaponProficiencyInfo_t *GetProficiencyValues();
+
+private:
+ CWeaponAR2( const CWeaponAR2 & );
+
+protected:
+
+ float m_flDelayedFire;
+ bool m_bShotDelayed;
+ int m_nVentPose;
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+};
+
+
+#endif //WEAPONAR2_H
diff --git a/mp/src/game/shared/hl2mp/weapon_crossbow.cpp b/mp/src/game/shared/hl2mp/weapon_crossbow.cpp
index a22b0529..5bada600 100644
--- a/mp/src/game/shared/hl2mp/weapon_crossbow.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_crossbow.cpp
@@ -1,956 +1,956 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "in_buttons.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
- #include "c_te_effect_dispatch.h"
-#else
- #include "hl2mp_player.h"
- #include "te_effect_dispatch.h"
- #include "IEffects.h"
- #include "Sprite.h"
- #include "SpriteTrail.h"
- #include "beam_shared.h"
-#endif
-
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-#include "effect_dispatch_data.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-//#define BOLT_MODEL "models/crossbow_bolt.mdl"
-#define BOLT_MODEL "models/weapons/w_missile_closed.mdl"
-
-#define BOLT_AIR_VELOCITY 3500
-#define BOLT_WATER_VELOCITY 1500
-#define BOLT_SKIN_NORMAL 0
-#define BOLT_SKIN_GLOW 1
-
-
-#ifndef CLIENT_DLL
-
-extern ConVar sk_plr_dmg_crossbow;
-extern ConVar sk_npc_dmg_crossbow;
-
-void TE_StickyBolt( IRecipientFilter& filter, float delay, Vector vecDirection, const Vector *origin );
-
-//-----------------------------------------------------------------------------
-// Crossbow Bolt
-//-----------------------------------------------------------------------------
-class CCrossbowBolt : public CBaseCombatCharacter
-{
- DECLARE_CLASS( CCrossbowBolt, CBaseCombatCharacter );
-
-public:
- CCrossbowBolt() { };
- ~CCrossbowBolt();
-
- Class_T Classify( void ) { return CLASS_NONE; }
-
-public:
- void Spawn( void );
- void Precache( void );
- void BubbleThink( void );
- void BoltTouch( CBaseEntity *pOther );
- bool CreateVPhysics( void );
- unsigned int PhysicsSolidMaskForEntity() const;
- static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, int iDamage, CBasePlayer *pentOwner = NULL );
-
-protected:
-
- bool CreateSprites( void );
-
- CHandle<CSprite> m_pGlowSprite;
- //CHandle<CSpriteTrail> m_pGlowTrail;
-
- int m_iDamage;
-
- DECLARE_DATADESC();
- DECLARE_SERVERCLASS();
-};
-LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt );
-
-BEGIN_DATADESC( CCrossbowBolt )
- // Function Pointers
- DEFINE_FUNCTION( BubbleThink ),
- DEFINE_FUNCTION( BoltTouch ),
-
- // These are recreated on reload, they don't need storage
- DEFINE_FIELD( m_pGlowSprite, FIELD_EHANDLE ),
- //DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ),
-
-END_DATADESC()
-
-IMPLEMENT_SERVERCLASS_ST( CCrossbowBolt, DT_CrossbowBolt )
-END_SEND_TABLE()
-
-CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, int iDamage, CBasePlayer *pentOwner )
-{
- // Create a new entity with CCrossbowBolt private data
- CCrossbowBolt *pBolt = (CCrossbowBolt *)CreateEntityByName( "crossbow_bolt" );
- UTIL_SetOrigin( pBolt, vecOrigin );
- pBolt->SetAbsAngles( angAngles );
- pBolt->Spawn();
- pBolt->SetOwnerEntity( pentOwner );
-
- pBolt->m_iDamage = iDamage;
-
- return pBolt;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CCrossbowBolt::~CCrossbowBolt( void )
-{
- if ( m_pGlowSprite )
- {
- UTIL_Remove( m_pGlowSprite );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CCrossbowBolt::CreateVPhysics( void )
-{
- // Create the object in the physics system
- VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-unsigned int CCrossbowBolt::PhysicsSolidMaskForEntity() const
-{
- return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CCrossbowBolt::CreateSprites( void )
-{
- // Start up the eye glow
- m_pGlowSprite = CSprite::SpriteCreate( "sprites/light_glow02_noz.vmt", GetLocalOrigin(), false );
-
- if ( m_pGlowSprite != NULL )
- {
- m_pGlowSprite->FollowEntity( this );
- m_pGlowSprite->SetTransparency( kRenderGlow, 255, 255, 255, 128, kRenderFxNoDissipation );
- m_pGlowSprite->SetScale( 0.2f );
- m_pGlowSprite->TurnOff();
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrossbowBolt::Spawn( void )
-{
- Precache( );
-
- SetModel( "models/crossbow_bolt.mdl" );
- SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
- UTIL_SetSize( this, -Vector(1,1,1), Vector(1,1,1) );
- SetSolid( SOLID_BBOX );
- SetGravity( 0.05f );
-
- // Make sure we're updated if we're underwater
- UpdateWaterState();
-
- SetTouch( &CCrossbowBolt::BoltTouch );
-
- SetThink( &CCrossbowBolt::BubbleThink );
- SetNextThink( gpGlobals->curtime + 0.1f );
-
- CreateSprites();
-
- // Make us glow until we've hit the wall
- m_nSkin = BOLT_SKIN_GLOW;
-}
-
-
-void CCrossbowBolt::Precache( void )
-{
- PrecacheModel( BOLT_MODEL );
-
- // This is used by C_TEStickyBolt, despte being different from above!!!
- PrecacheModel( "models/crossbow_bolt.mdl" );
-
- PrecacheModel( "sprites/light_glow02_noz.vmt" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pOther -
-//-----------------------------------------------------------------------------
-void CCrossbowBolt::BoltTouch( CBaseEntity *pOther )
-{
- if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
- return;
-
- if ( pOther->m_takedamage != DAMAGE_NO )
- {
- trace_t tr, tr2;
- tr = BaseClass::GetTouchTrace();
- Vector vecNormalizedVel = GetAbsVelocity();
-
- ClearMultiDamage();
- VectorNormalize( vecNormalizedVel );
-
- if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->IsNPC() )
- {
- CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_iDamage, DMG_NEVERGIB );
- dmgInfo.AdjustPlayerDamageInflictedForSkillLevel();
- CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f );
- dmgInfo.SetDamagePosition( tr.endpos );
- pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr );
- }
- else
- {
- CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_iDamage, DMG_BULLET | DMG_NEVERGIB );
- CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f );
- dmgInfo.SetDamagePosition( tr.endpos );
- pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr );
- }
-
- ApplyMultiDamage();
-
- //Adrian: keep going through the glass.
- if ( pOther->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS )
- return;
-
- SetAbsVelocity( Vector( 0, 0, 0 ) );
-
- // play body "thwack" sound
- EmitSound( "Weapon_Crossbow.BoltHitBody" );
-
- Vector vForward;
-
- AngleVectors( GetAbsAngles(), &vForward );
- VectorNormalize ( vForward );
-
- UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vForward * 128, MASK_OPAQUE, pOther, COLLISION_GROUP_NONE, &tr2 );
-
- if ( tr2.fraction != 1.0f )
- {
-// NDebugOverlay::Box( tr2.endpos, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 255, 0, 0, 10 );
-// NDebugOverlay::Box( GetAbsOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 0, 255, 0, 10 );
-
- if ( tr2.m_pEnt == NULL || ( tr2.m_pEnt && tr2.m_pEnt->GetMoveType() == MOVETYPE_NONE ) )
- {
- CEffectData data;
-
- data.m_vOrigin = tr2.endpos;
- data.m_vNormal = vForward;
- data.m_nEntIndex = tr2.fraction != 1.0f;
-
- DispatchEffect( "BoltImpact", data );
- }
- }
-
- SetTouch( NULL );
- SetThink( NULL );
-
- UTIL_Remove( this );
- }
- else
- {
- trace_t tr;
- tr = BaseClass::GetTouchTrace();
-
- // See if we struck the world
- if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) )
- {
- EmitSound( "Weapon_Crossbow.BoltHitWorld" );
-
- // if what we hit is static architecture, can stay around for a while.
- Vector vecDir = GetAbsVelocity();
- float speed = VectorNormalize( vecDir );
-
- // See if we should reflect off this surface
- float hitDot = DotProduct( tr.plane.normal, -vecDir );
-
- if ( ( hitDot < 0.5f ) && ( speed > 100 ) )
- {
- Vector vReflection = 2.0f * tr.plane.normal * hitDot + vecDir;
-
- QAngle reflectAngles;
-
- VectorAngles( vReflection, reflectAngles );
-
- SetLocalAngles( reflectAngles );
-
- SetAbsVelocity( vReflection * speed * 0.75f );
-
- // Start to sink faster
- SetGravity( 1.0f );
- }
- else
- {
- SetThink( &CCrossbowBolt::SUB_Remove );
- SetNextThink( gpGlobals->curtime + 2.0f );
-
- //FIXME: We actually want to stick (with hierarchy) to what we've hit
- SetMoveType( MOVETYPE_NONE );
-
- Vector vForward;
-
- AngleVectors( GetAbsAngles(), &vForward );
- VectorNormalize ( vForward );
-
- CEffectData data;
-
- data.m_vOrigin = tr.endpos;
- data.m_vNormal = vForward;
- data.m_nEntIndex = 0;
-
- DispatchEffect( "BoltImpact", data );
-
- UTIL_ImpactTrace( &tr, DMG_BULLET );
-
- AddEffects( EF_NODRAW );
- SetTouch( NULL );
- SetThink( &CCrossbowBolt::SUB_Remove );
- SetNextThink( gpGlobals->curtime + 2.0f );
-
- if ( m_pGlowSprite != NULL )
- {
- m_pGlowSprite->TurnOn();
- m_pGlowSprite->FadeAndDie( 3.0f );
- }
- }
-
- // Shoot some sparks
- if ( UTIL_PointContents( GetAbsOrigin() ) != CONTENTS_WATER)
- {
- g_pEffects->Sparks( GetAbsOrigin() );
- }
- }
- else
- {
- // Put a mark unless we've hit the sky
- if ( ( tr.surface.flags & SURF_SKY ) == false )
- {
- UTIL_ImpactTrace( &tr, DMG_BULLET );
- }
-
- UTIL_Remove( this );
- }
- }
-
- if ( g_pGameRules->IsMultiplayer() )
- {
-// SetThink( &CCrossbowBolt::ExplodeThink );
-// SetNextThink( gpGlobals->curtime + 0.1f );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CCrossbowBolt::BubbleThink( void )
-{
- QAngle angNewAngles;
-
- VectorAngles( GetAbsVelocity(), angNewAngles );
- SetAbsAngles( angNewAngles );
-
- SetNextThink( gpGlobals->curtime + 0.1f );
-
- if ( GetWaterLevel() == 0 )
- return;
-
- UTIL_BubbleTrail( GetAbsOrigin() - GetAbsVelocity() * 0.1f, GetAbsOrigin(), 5 );
-}
-
-#endif
-
-//-----------------------------------------------------------------------------
-// CWeaponCrossbow
-//-----------------------------------------------------------------------------
-
-#ifdef CLIENT_DLL
-#define CWeaponCrossbow C_WeaponCrossbow
-#endif
-
-class CWeaponCrossbow : public CBaseHL2MPCombatWeapon
-{
- DECLARE_CLASS( CWeaponCrossbow, CBaseHL2MPCombatWeapon );
-public:
-
- CWeaponCrossbow( void );
-
- virtual void Precache( void );
- virtual void PrimaryAttack( void );
- virtual void SecondaryAttack( void );
- virtual bool Deploy( void );
- virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
- virtual bool Reload( void );
- virtual void ItemPostFrame( void );
- virtual void ItemBusyFrame( void );
- virtual bool SendWeaponAnim( int iActivity );
-
-#ifndef CLIENT_DLL
- virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
-#endif
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
-private:
-
- void SetSkin( int skinNum );
- void CheckZoomToggle( void );
- void FireBolt( void );
- void ToggleZoom( void );
-
- // Various states for the crossbow's charger
- enum ChargerState_t
- {
- CHARGER_STATE_START_LOAD,
- CHARGER_STATE_START_CHARGE,
- CHARGER_STATE_READY,
- CHARGER_STATE_DISCHARGE,
- CHARGER_STATE_OFF,
- };
-
- void CreateChargerEffects( void );
- void SetChargerState( ChargerState_t state );
- void DoLoadEffect( void );
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-
-private:
-
- // Charger effects
- ChargerState_t m_nChargeState;
-
-#ifndef CLIENT_DLL
- CHandle<CSprite> m_hChargerSprite;
-#endif
-
- CNetworkVar( bool, m_bInZoom );
- CNetworkVar( bool, m_bMustReload );
-
- CWeaponCrossbow( const CWeaponCrossbow & );
-};
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrossbow, DT_WeaponCrossbow )
-
-BEGIN_NETWORK_TABLE( CWeaponCrossbow, DT_WeaponCrossbow )
-#ifdef CLIENT_DLL
- RecvPropBool( RECVINFO( m_bInZoom ) ),
- RecvPropBool( RECVINFO( m_bMustReload ) ),
-#else
- SendPropBool( SENDINFO( m_bInZoom ) ),
- SendPropBool( SENDINFO( m_bMustReload ) ),
-#endif
-END_NETWORK_TABLE()
-
-#ifdef CLIENT_DLL
-BEGIN_PREDICTION_DATA( CWeaponCrossbow )
- DEFINE_PRED_FIELD( m_bInZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bMustReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
-END_PREDICTION_DATA()
-#endif
-
-LINK_ENTITY_TO_CLASS( weapon_crossbow, CWeaponCrossbow );
-
-PRECACHE_WEAPON_REGISTER( weapon_crossbow );
-
-#ifndef CLIENT_DLL
-
-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 },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponCrossbow);
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-//-----------------------------------------------------------------------------
-CWeaponCrossbow::CWeaponCrossbow( void )
-{
- m_bReloadsSingly = true;
- m_bFiresUnderwater = true;
- m_bInZoom = false;
- m_bMustReload = false;
-}
-
-#define CROSSBOW_GLOW_SPRITE "sprites/light_glow02_noz.vmt"
-#define CROSSBOW_GLOW_SPRITE2 "sprites/blueflare1.vmt"
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::Precache( void )
-{
-#ifndef CLIENT_DLL
- UTIL_PrecacheOther( "crossbow_bolt" );
-#endif
-
- PrecacheScriptSound( "Weapon_Crossbow.BoltHitBody" );
- PrecacheScriptSound( "Weapon_Crossbow.BoltHitWorld" );
- PrecacheScriptSound( "Weapon_Crossbow.BoltSkewer" );
-
- PrecacheModel( CROSSBOW_GLOW_SPRITE );
- PrecacheModel( CROSSBOW_GLOW_SPRITE2 );
-
- BaseClass::Precache();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::PrimaryAttack( void )
-{
- if ( m_bInZoom && g_pGameRules->IsMultiplayer() )
- {
-// FireSniperBolt();
- FireBolt();
- }
- else
- {
- FireBolt();
- }
-
- // Signal a reload
- m_bMustReload = true;
-
- SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::SecondaryAttack( void )
-{
- //NOTENOTE: The zooming is handled by the post/busy frames
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponCrossbow::Reload( void )
-{
- if ( BaseClass::Reload() )
- {
- m_bMustReload = false;
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::CheckZoomToggle( void )
-{
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
- {
- ToggleZoom();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::ItemBusyFrame( void )
-{
- // Allow zoom toggling even when we're reloading
- CheckZoomToggle();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::ItemPostFrame( void )
-{
- // Allow zoom toggling
- CheckZoomToggle();
-
- if ( m_bMustReload && HasWeaponIdleTimeElapsed() )
- {
- Reload();
- }
-
- BaseClass::ItemPostFrame();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::FireBolt( void )
-{
- if ( m_iClip1 <= 0 )
- {
- if ( !m_bFireOnEmpty )
- {
- Reload();
- }
- else
- {
- WeaponSound( EMPTY );
- m_flNextPrimaryAttack = 0.15;
- }
-
- return;
- }
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
-#ifndef CLIENT_DLL
- Vector vecAiming = pOwner->GetAutoaimVector( 0 );
- Vector vecSrc = pOwner->Weapon_ShootPosition();
-
- QAngle angAiming;
- VectorAngles( vecAiming, angAiming );
-
- CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate( vecSrc, angAiming, GetHL2MPWpnData().m_iPlayerDamage, pOwner );
-
- if ( pOwner->GetWaterLevel() == 3 )
- {
- pBolt->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
- }
- else
- {
- pBolt->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
- }
-
-#endif
-
- m_iClip1--;
-
- pOwner->ViewPunch( QAngle( -2, 0, 0 ) );
-
- WeaponSound( SINGLE );
- WeaponSound( SPECIAL2 );
-
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
-
- if ( !m_iClip1 && pOwner->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
- {
- // HEV suit - indicate out of ammo condition
- pOwner->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
- }
-
- m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
-
- DoLoadEffect();
- SetChargerState( CHARGER_STATE_DISCHARGE );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponCrossbow::Deploy( void )
-{
- if ( m_iClip1 <= 0 )
- {
- return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
- }
-
- SetSkin( BOLT_SKIN_GLOW );
-
- return BaseClass::Deploy();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pSwitchingTo -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponCrossbow::Holster( CBaseCombatWeapon *pSwitchingTo )
-{
- if ( m_bInZoom )
- {
- ToggleZoom();
- }
-
- SetChargerState( CHARGER_STATE_OFF );
-
- return BaseClass::Holster( pSwitchingTo );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::ToggleZoom( void )
-{
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if ( pPlayer == NULL )
- return;
-
-#ifndef CLIENT_DLL
-
- if ( m_bInZoom )
- {
- if ( pPlayer->SetFOV( this, 0, 0.2f ) )
- {
- m_bInZoom = false;
- }
- }
- else
- {
- if ( pPlayer->SetFOV( this, 20, 0.1f ) )
- {
- m_bInZoom = true;
- }
- }
-#endif
-}
-
-#define BOLT_TIP_ATTACHMENT 2
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::CreateChargerEffects( void )
-{
-#ifndef CLIENT_DLL
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( m_hChargerSprite != NULL )
- return;
-
- m_hChargerSprite = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE, GetAbsOrigin(), false );
-
- if ( m_hChargerSprite )
- {
- m_hChargerSprite->SetAttachment( pOwner->GetViewModel(), BOLT_TIP_ATTACHMENT );
- m_hChargerSprite->SetTransparency( kRenderTransAdd, 255, 128, 0, 255, kRenderFxNoDissipation );
- m_hChargerSprite->SetBrightness( 0 );
- m_hChargerSprite->SetScale( 0.1f );
- m_hChargerSprite->TurnOff();
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : skinNum -
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::SetSkin( int skinNum )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- CBaseViewModel *pViewModel = pOwner->GetViewModel();
-
- if ( pViewModel == NULL )
- return;
-
- pViewModel->m_nSkin = skinNum;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::DoLoadEffect( void )
-{
- SetSkin( BOLT_SKIN_GLOW );
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- CBaseViewModel *pViewModel = pOwner->GetViewModel();
-
- if ( pViewModel == NULL )
- return;
-
- CEffectData data;
-
-#ifdef CLIENT_DLL
- data.m_hEntity = pViewModel->GetRefEHandle();
-#else
- data.m_nEntIndex = pViewModel->entindex();
-#endif
- data.m_nAttachmentIndex = 1;
-
- DispatchEffect( "CrossbowLoad", data );
-
-#ifndef CLIENT_DLL
-
- CSprite *pBlast = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE2, GetAbsOrigin(), false );
-
- if ( pBlast )
- {
- pBlast->SetAttachment( pOwner->GetViewModel(), 1 );
- pBlast->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
- pBlast->SetBrightness( 128 );
- pBlast->SetScale( 0.2f );
- pBlast->FadeOutFromSpawn();
- }
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : state -
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::SetChargerState( ChargerState_t state )
-{
- // Make sure we're setup
- CreateChargerEffects();
-
- // Don't do this twice
- if ( state == m_nChargeState )
- return;
-
- m_nChargeState = state;
-
- switch( m_nChargeState )
- {
- case CHARGER_STATE_START_LOAD:
-
- WeaponSound( SPECIAL1 );
-
- // Shoot some sparks and draw a beam between the two outer points
- DoLoadEffect();
-
- break;
-#ifndef CLIENT_DLL
- case CHARGER_STATE_START_CHARGE:
- {
- if ( m_hChargerSprite == NULL )
- break;
-
- m_hChargerSprite->SetBrightness( 32, 0.5f );
- m_hChargerSprite->SetScale( 0.025f, 0.5f );
- m_hChargerSprite->TurnOn();
- }
-
- break;
-
- case CHARGER_STATE_READY:
- {
- // Get fully charged
- if ( m_hChargerSprite == NULL )
- break;
-
- m_hChargerSprite->SetBrightness( 80, 1.0f );
- m_hChargerSprite->SetScale( 0.1f, 0.5f );
- m_hChargerSprite->TurnOn();
- }
-
- break;
-
- case CHARGER_STATE_DISCHARGE:
- {
- SetSkin( BOLT_SKIN_NORMAL );
-
- if ( m_hChargerSprite == NULL )
- break;
-
- m_hChargerSprite->SetBrightness( 0 );
- m_hChargerSprite->TurnOff();
- }
-
- break;
-#endif
- case CHARGER_STATE_OFF:
- {
- SetSkin( BOLT_SKIN_NORMAL );
-
-#ifndef CLIENT_DLL
- if ( m_hChargerSprite == NULL )
- break;
-
- m_hChargerSprite->SetBrightness( 0 );
- m_hChargerSprite->TurnOff();
-#endif
- }
- break;
-
- default:
- break;
- }
-}
-
-#ifndef CLIENT_DLL
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEvent -
-// *pOperator -
-//-----------------------------------------------------------------------------
-void CWeaponCrossbow::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
-{
- switch( pEvent->event )
- {
- case EVENT_WEAPON_THROW:
- SetChargerState( CHARGER_STATE_START_LOAD );
- break;
-
- case EVENT_WEAPON_THROW2:
- SetChargerState( CHARGER_STATE_START_CHARGE );
- break;
-
- case EVENT_WEAPON_THROW3:
- SetChargerState( CHARGER_STATE_READY );
- break;
-
- 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
-//-----------------------------------------------------------------------------
-bool CWeaponCrossbow::SendWeaponAnim( int iActivity )
-{
- int newActivity = iActivity;
-
- // The last shot needs a non-loaded activity
- if ( ( newActivity == ACT_VM_IDLE ) && ( m_iClip1 <= 0 ) )
- {
- newActivity = ACT_VM_FIDGET;
- }
-
- //For now, just set the ideal activity and be done with it
- return BaseClass::SendWeaponAnim( newActivity );
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+ #include "c_te_effect_dispatch.h"
+#else
+ #include "hl2mp_player.h"
+ #include "te_effect_dispatch.h"
+ #include "IEffects.h"
+ #include "Sprite.h"
+ #include "SpriteTrail.h"
+ #include "beam_shared.h"
+#endif
+
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+#include "effect_dispatch_data.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//#define BOLT_MODEL "models/crossbow_bolt.mdl"
+#define BOLT_MODEL "models/weapons/w_missile_closed.mdl"
+
+#define BOLT_AIR_VELOCITY 3500
+#define BOLT_WATER_VELOCITY 1500
+#define BOLT_SKIN_NORMAL 0
+#define BOLT_SKIN_GLOW 1
+
+
+#ifndef CLIENT_DLL
+
+extern ConVar sk_plr_dmg_crossbow;
+extern ConVar sk_npc_dmg_crossbow;
+
+void TE_StickyBolt( IRecipientFilter& filter, float delay, Vector vecDirection, const Vector *origin );
+
+//-----------------------------------------------------------------------------
+// Crossbow Bolt
+//-----------------------------------------------------------------------------
+class CCrossbowBolt : public CBaseCombatCharacter
+{
+ DECLARE_CLASS( CCrossbowBolt, CBaseCombatCharacter );
+
+public:
+ CCrossbowBolt() { };
+ ~CCrossbowBolt();
+
+ Class_T Classify( void ) { return CLASS_NONE; }
+
+public:
+ void Spawn( void );
+ void Precache( void );
+ void BubbleThink( void );
+ void BoltTouch( CBaseEntity *pOther );
+ bool CreateVPhysics( void );
+ unsigned int PhysicsSolidMaskForEntity() const;
+ static CCrossbowBolt *BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, int iDamage, CBasePlayer *pentOwner = NULL );
+
+protected:
+
+ bool CreateSprites( void );
+
+ CHandle<CSprite> m_pGlowSprite;
+ //CHandle<CSpriteTrail> m_pGlowTrail;
+
+ int m_iDamage;
+
+ DECLARE_DATADESC();
+ DECLARE_SERVERCLASS();
+};
+LINK_ENTITY_TO_CLASS( crossbow_bolt, CCrossbowBolt );
+
+BEGIN_DATADESC( CCrossbowBolt )
+ // Function Pointers
+ DEFINE_FUNCTION( BubbleThink ),
+ DEFINE_FUNCTION( BoltTouch ),
+
+ // These are recreated on reload, they don't need storage
+ DEFINE_FIELD( m_pGlowSprite, FIELD_EHANDLE ),
+ //DEFINE_FIELD( m_pGlowTrail, FIELD_EHANDLE ),
+
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST( CCrossbowBolt, DT_CrossbowBolt )
+END_SEND_TABLE()
+
+CCrossbowBolt *CCrossbowBolt::BoltCreate( const Vector &vecOrigin, const QAngle &angAngles, int iDamage, CBasePlayer *pentOwner )
+{
+ // Create a new entity with CCrossbowBolt private data
+ CCrossbowBolt *pBolt = (CCrossbowBolt *)CreateEntityByName( "crossbow_bolt" );
+ UTIL_SetOrigin( pBolt, vecOrigin );
+ pBolt->SetAbsAngles( angAngles );
+ pBolt->Spawn();
+ pBolt->SetOwnerEntity( pentOwner );
+
+ pBolt->m_iDamage = iDamage;
+
+ return pBolt;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CCrossbowBolt::~CCrossbowBolt( void )
+{
+ if ( m_pGlowSprite )
+ {
+ UTIL_Remove( m_pGlowSprite );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CCrossbowBolt::CreateVPhysics( void )
+{
+ // Create the object in the physics system
+ VPhysicsInitNormal( SOLID_BBOX, FSOLID_NOT_STANDABLE, false );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+unsigned int CCrossbowBolt::PhysicsSolidMaskForEntity() const
+{
+ return ( BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX ) & ~CONTENTS_GRATE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CCrossbowBolt::CreateSprites( void )
+{
+ // Start up the eye glow
+ m_pGlowSprite = CSprite::SpriteCreate( "sprites/light_glow02_noz.vmt", GetLocalOrigin(), false );
+
+ if ( m_pGlowSprite != NULL )
+ {
+ m_pGlowSprite->FollowEntity( this );
+ m_pGlowSprite->SetTransparency( kRenderGlow, 255, 255, 255, 128, kRenderFxNoDissipation );
+ m_pGlowSprite->SetScale( 0.2f );
+ m_pGlowSprite->TurnOff();
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCrossbowBolt::Spawn( void )
+{
+ Precache( );
+
+ SetModel( "models/crossbow_bolt.mdl" );
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+ UTIL_SetSize( this, -Vector(1,1,1), Vector(1,1,1) );
+ SetSolid( SOLID_BBOX );
+ SetGravity( 0.05f );
+
+ // Make sure we're updated if we're underwater
+ UpdateWaterState();
+
+ SetTouch( &CCrossbowBolt::BoltTouch );
+
+ SetThink( &CCrossbowBolt::BubbleThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ CreateSprites();
+
+ // Make us glow until we've hit the wall
+ m_nSkin = BOLT_SKIN_GLOW;
+}
+
+
+void CCrossbowBolt::Precache( void )
+{
+ PrecacheModel( BOLT_MODEL );
+
+ // This is used by C_TEStickyBolt, despte being different from above!!!
+ PrecacheModel( "models/crossbow_bolt.mdl" );
+
+ PrecacheModel( "sprites/light_glow02_noz.vmt" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CCrossbowBolt::BoltTouch( CBaseEntity *pOther )
+{
+ if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
+ return;
+
+ if ( pOther->m_takedamage != DAMAGE_NO )
+ {
+ trace_t tr, tr2;
+ tr = BaseClass::GetTouchTrace();
+ Vector vecNormalizedVel = GetAbsVelocity();
+
+ ClearMultiDamage();
+ VectorNormalize( vecNormalizedVel );
+
+ if( GetOwnerEntity() && GetOwnerEntity()->IsPlayer() && pOther->IsNPC() )
+ {
+ CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_iDamage, DMG_NEVERGIB );
+ dmgInfo.AdjustPlayerDamageInflictedForSkillLevel();
+ CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f );
+ dmgInfo.SetDamagePosition( tr.endpos );
+ pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr );
+ }
+ else
+ {
+ CTakeDamageInfo dmgInfo( this, GetOwnerEntity(), m_iDamage, DMG_BULLET | DMG_NEVERGIB );
+ CalculateMeleeDamageForce( &dmgInfo, vecNormalizedVel, tr.endpos, 0.7f );
+ dmgInfo.SetDamagePosition( tr.endpos );
+ pOther->DispatchTraceAttack( dmgInfo, vecNormalizedVel, &tr );
+ }
+
+ ApplyMultiDamage();
+
+ //Adrian: keep going through the glass.
+ if ( pOther->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS )
+ return;
+
+ SetAbsVelocity( Vector( 0, 0, 0 ) );
+
+ // play body "thwack" sound
+ EmitSound( "Weapon_Crossbow.BoltHitBody" );
+
+ Vector vForward;
+
+ AngleVectors( GetAbsAngles(), &vForward );
+ VectorNormalize ( vForward );
+
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + vForward * 128, MASK_OPAQUE, pOther, COLLISION_GROUP_NONE, &tr2 );
+
+ if ( tr2.fraction != 1.0f )
+ {
+// NDebugOverlay::Box( tr2.endpos, Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 255, 0, 0, 10 );
+// NDebugOverlay::Box( GetAbsOrigin(), Vector( -16, -16, -16 ), Vector( 16, 16, 16 ), 0, 0, 255, 0, 10 );
+
+ if ( tr2.m_pEnt == NULL || ( tr2.m_pEnt && tr2.m_pEnt->GetMoveType() == MOVETYPE_NONE ) )
+ {
+ CEffectData data;
+
+ data.m_vOrigin = tr2.endpos;
+ data.m_vNormal = vForward;
+ data.m_nEntIndex = tr2.fraction != 1.0f;
+
+ DispatchEffect( "BoltImpact", data );
+ }
+ }
+
+ SetTouch( NULL );
+ SetThink( NULL );
+
+ UTIL_Remove( this );
+ }
+ else
+ {
+ trace_t tr;
+ tr = BaseClass::GetTouchTrace();
+
+ // See if we struck the world
+ if ( pOther->GetMoveType() == MOVETYPE_NONE && !( tr.surface.flags & SURF_SKY ) )
+ {
+ EmitSound( "Weapon_Crossbow.BoltHitWorld" );
+
+ // if what we hit is static architecture, can stay around for a while.
+ Vector vecDir = GetAbsVelocity();
+ float speed = VectorNormalize( vecDir );
+
+ // See if we should reflect off this surface
+ float hitDot = DotProduct( tr.plane.normal, -vecDir );
+
+ if ( ( hitDot < 0.5f ) && ( speed > 100 ) )
+ {
+ Vector vReflection = 2.0f * tr.plane.normal * hitDot + vecDir;
+
+ QAngle reflectAngles;
+
+ VectorAngles( vReflection, reflectAngles );
+
+ SetLocalAngles( reflectAngles );
+
+ SetAbsVelocity( vReflection * speed * 0.75f );
+
+ // Start to sink faster
+ SetGravity( 1.0f );
+ }
+ else
+ {
+ SetThink( &CCrossbowBolt::SUB_Remove );
+ SetNextThink( gpGlobals->curtime + 2.0f );
+
+ //FIXME: We actually want to stick (with hierarchy) to what we've hit
+ SetMoveType( MOVETYPE_NONE );
+
+ Vector vForward;
+
+ AngleVectors( GetAbsAngles(), &vForward );
+ VectorNormalize ( vForward );
+
+ CEffectData data;
+
+ data.m_vOrigin = tr.endpos;
+ data.m_vNormal = vForward;
+ data.m_nEntIndex = 0;
+
+ DispatchEffect( "BoltImpact", data );
+
+ UTIL_ImpactTrace( &tr, DMG_BULLET );
+
+ AddEffects( EF_NODRAW );
+ SetTouch( NULL );
+ SetThink( &CCrossbowBolt::SUB_Remove );
+ SetNextThink( gpGlobals->curtime + 2.0f );
+
+ if ( m_pGlowSprite != NULL )
+ {
+ m_pGlowSprite->TurnOn();
+ m_pGlowSprite->FadeAndDie( 3.0f );
+ }
+ }
+
+ // Shoot some sparks
+ if ( UTIL_PointContents( GetAbsOrigin() ) != CONTENTS_WATER)
+ {
+ g_pEffects->Sparks( GetAbsOrigin() );
+ }
+ }
+ else
+ {
+ // Put a mark unless we've hit the sky
+ if ( ( tr.surface.flags & SURF_SKY ) == false )
+ {
+ UTIL_ImpactTrace( &tr, DMG_BULLET );
+ }
+
+ UTIL_Remove( this );
+ }
+ }
+
+ if ( g_pGameRules->IsMultiplayer() )
+ {
+// SetThink( &CCrossbowBolt::ExplodeThink );
+// SetNextThink( gpGlobals->curtime + 0.1f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCrossbowBolt::BubbleThink( void )
+{
+ QAngle angNewAngles;
+
+ VectorAngles( GetAbsVelocity(), angNewAngles );
+ SetAbsAngles( angNewAngles );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ if ( GetWaterLevel() == 0 )
+ return;
+
+ UTIL_BubbleTrail( GetAbsOrigin() - GetAbsVelocity() * 0.1f, GetAbsOrigin(), 5 );
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// CWeaponCrossbow
+//-----------------------------------------------------------------------------
+
+#ifdef CLIENT_DLL
+#define CWeaponCrossbow C_WeaponCrossbow
+#endif
+
+class CWeaponCrossbow : public CBaseHL2MPCombatWeapon
+{
+ DECLARE_CLASS( CWeaponCrossbow, CBaseHL2MPCombatWeapon );
+public:
+
+ CWeaponCrossbow( void );
+
+ virtual void Precache( void );
+ virtual void PrimaryAttack( void );
+ virtual void SecondaryAttack( void );
+ virtual bool Deploy( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ virtual bool Reload( void );
+ virtual void ItemPostFrame( void );
+ virtual void ItemBusyFrame( void );
+ virtual bool SendWeaponAnim( int iActivity );
+
+#ifndef CLIENT_DLL
+ virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
+#endif
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+private:
+
+ void SetSkin( int skinNum );
+ void CheckZoomToggle( void );
+ void FireBolt( void );
+ void ToggleZoom( void );
+
+ // Various states for the crossbow's charger
+ enum ChargerState_t
+ {
+ CHARGER_STATE_START_LOAD,
+ CHARGER_STATE_START_CHARGE,
+ CHARGER_STATE_READY,
+ CHARGER_STATE_DISCHARGE,
+ CHARGER_STATE_OFF,
+ };
+
+ void CreateChargerEffects( void );
+ void SetChargerState( ChargerState_t state );
+ void DoLoadEffect( void );
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+
+private:
+
+ // Charger effects
+ ChargerState_t m_nChargeState;
+
+#ifndef CLIENT_DLL
+ CHandle<CSprite> m_hChargerSprite;
+#endif
+
+ CNetworkVar( bool, m_bInZoom );
+ CNetworkVar( bool, m_bMustReload );
+
+ CWeaponCrossbow( const CWeaponCrossbow & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrossbow, DT_WeaponCrossbow )
+
+BEGIN_NETWORK_TABLE( CWeaponCrossbow, DT_WeaponCrossbow )
+#ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bInZoom ) ),
+ RecvPropBool( RECVINFO( m_bMustReload ) ),
+#else
+ SendPropBool( SENDINFO( m_bInZoom ) ),
+ SendPropBool( SENDINFO( m_bMustReload ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponCrossbow )
+ DEFINE_PRED_FIELD( m_bInZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bMustReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_crossbow, CWeaponCrossbow );
+
+PRECACHE_WEAPON_REGISTER( weapon_crossbow );
+
+#ifndef CLIENT_DLL
+
+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 },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponCrossbow);
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CWeaponCrossbow::CWeaponCrossbow( void )
+{
+ m_bReloadsSingly = true;
+ m_bFiresUnderwater = true;
+ m_bInZoom = false;
+ m_bMustReload = false;
+}
+
+#define CROSSBOW_GLOW_SPRITE "sprites/light_glow02_noz.vmt"
+#define CROSSBOW_GLOW_SPRITE2 "sprites/blueflare1.vmt"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::Precache( void )
+{
+#ifndef CLIENT_DLL
+ UTIL_PrecacheOther( "crossbow_bolt" );
+#endif
+
+ PrecacheScriptSound( "Weapon_Crossbow.BoltHitBody" );
+ PrecacheScriptSound( "Weapon_Crossbow.BoltHitWorld" );
+ PrecacheScriptSound( "Weapon_Crossbow.BoltSkewer" );
+
+ PrecacheModel( CROSSBOW_GLOW_SPRITE );
+ PrecacheModel( CROSSBOW_GLOW_SPRITE2 );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::PrimaryAttack( void )
+{
+ if ( m_bInZoom && g_pGameRules->IsMultiplayer() )
+ {
+// FireSniperBolt();
+ FireBolt();
+ }
+ else
+ {
+ FireBolt();
+ }
+
+ // Signal a reload
+ m_bMustReload = true;
+
+ SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration( ACT_VM_PRIMARYATTACK ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::SecondaryAttack( void )
+{
+ //NOTENOTE: The zooming is handled by the post/busy frames
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponCrossbow::Reload( void )
+{
+ if ( BaseClass::Reload() )
+ {
+ m_bMustReload = false;
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::CheckZoomToggle( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if ( pPlayer->m_afButtonPressed & IN_ATTACK2 )
+ {
+ ToggleZoom();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::ItemBusyFrame( void )
+{
+ // Allow zoom toggling even when we're reloading
+ CheckZoomToggle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::ItemPostFrame( void )
+{
+ // Allow zoom toggling
+ CheckZoomToggle();
+
+ if ( m_bMustReload && HasWeaponIdleTimeElapsed() )
+ {
+ Reload();
+ }
+
+ BaseClass::ItemPostFrame();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::FireBolt( void )
+{
+ if ( m_iClip1 <= 0 )
+ {
+ if ( !m_bFireOnEmpty )
+ {
+ Reload();
+ }
+ else
+ {
+ WeaponSound( EMPTY );
+ m_flNextPrimaryAttack = 0.15;
+ }
+
+ return;
+ }
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+#ifndef CLIENT_DLL
+ Vector vecAiming = pOwner->GetAutoaimVector( 0 );
+ Vector vecSrc = pOwner->Weapon_ShootPosition();
+
+ QAngle angAiming;
+ VectorAngles( vecAiming, angAiming );
+
+ CCrossbowBolt *pBolt = CCrossbowBolt::BoltCreate( vecSrc, angAiming, GetHL2MPWpnData().m_iPlayerDamage, pOwner );
+
+ if ( pOwner->GetWaterLevel() == 3 )
+ {
+ pBolt->SetAbsVelocity( vecAiming * BOLT_WATER_VELOCITY );
+ }
+ else
+ {
+ pBolt->SetAbsVelocity( vecAiming * BOLT_AIR_VELOCITY );
+ }
+
+#endif
+
+ m_iClip1--;
+
+ pOwner->ViewPunch( QAngle( -2, 0, 0 ) );
+
+ WeaponSound( SINGLE );
+ WeaponSound( SPECIAL2 );
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ if ( !m_iClip1 && pOwner->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
+ {
+ // HEV suit - indicate out of ammo condition
+ pOwner->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
+ }
+
+ m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + 0.75;
+
+ DoLoadEffect();
+ SetChargerState( CHARGER_STATE_DISCHARGE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponCrossbow::Deploy( void )
+{
+ if ( m_iClip1 <= 0 )
+ {
+ return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_CROSSBOW_DRAW_UNLOADED, (char*)GetAnimPrefix() );
+ }
+
+ SetSkin( BOLT_SKIN_GLOW );
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pSwitchingTo -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponCrossbow::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ if ( m_bInZoom )
+ {
+ ToggleZoom();
+ }
+
+ SetChargerState( CHARGER_STATE_OFF );
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::ToggleZoom( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if ( pPlayer == NULL )
+ return;
+
+#ifndef CLIENT_DLL
+
+ if ( m_bInZoom )
+ {
+ if ( pPlayer->SetFOV( this, 0, 0.2f ) )
+ {
+ m_bInZoom = false;
+ }
+ }
+ else
+ {
+ if ( pPlayer->SetFOV( this, 20, 0.1f ) )
+ {
+ m_bInZoom = true;
+ }
+ }
+#endif
+}
+
+#define BOLT_TIP_ATTACHMENT 2
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::CreateChargerEffects( void )
+{
+#ifndef CLIENT_DLL
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( m_hChargerSprite != NULL )
+ return;
+
+ m_hChargerSprite = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE, GetAbsOrigin(), false );
+
+ if ( m_hChargerSprite )
+ {
+ m_hChargerSprite->SetAttachment( pOwner->GetViewModel(), BOLT_TIP_ATTACHMENT );
+ m_hChargerSprite->SetTransparency( kRenderTransAdd, 255, 128, 0, 255, kRenderFxNoDissipation );
+ m_hChargerSprite->SetBrightness( 0 );
+ m_hChargerSprite->SetScale( 0.1f );
+ m_hChargerSprite->TurnOff();
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : skinNum -
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::SetSkin( int skinNum )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ CBaseViewModel *pViewModel = pOwner->GetViewModel();
+
+ if ( pViewModel == NULL )
+ return;
+
+ pViewModel->m_nSkin = skinNum;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::DoLoadEffect( void )
+{
+ SetSkin( BOLT_SKIN_GLOW );
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ CBaseViewModel *pViewModel = pOwner->GetViewModel();
+
+ if ( pViewModel == NULL )
+ return;
+
+ CEffectData data;
+
+#ifdef CLIENT_DLL
+ data.m_hEntity = pViewModel->GetRefEHandle();
+#else
+ data.m_nEntIndex = pViewModel->entindex();
+#endif
+ data.m_nAttachmentIndex = 1;
+
+ DispatchEffect( "CrossbowLoad", data );
+
+#ifndef CLIENT_DLL
+
+ CSprite *pBlast = CSprite::SpriteCreate( CROSSBOW_GLOW_SPRITE2, GetAbsOrigin(), false );
+
+ if ( pBlast )
+ {
+ pBlast->SetAttachment( pOwner->GetViewModel(), 1 );
+ pBlast->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxNone );
+ pBlast->SetBrightness( 128 );
+ pBlast->SetScale( 0.2f );
+ pBlast->FadeOutFromSpawn();
+ }
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::SetChargerState( ChargerState_t state )
+{
+ // Make sure we're setup
+ CreateChargerEffects();
+
+ // Don't do this twice
+ if ( state == m_nChargeState )
+ return;
+
+ m_nChargeState = state;
+
+ switch( m_nChargeState )
+ {
+ case CHARGER_STATE_START_LOAD:
+
+ WeaponSound( SPECIAL1 );
+
+ // Shoot some sparks and draw a beam between the two outer points
+ DoLoadEffect();
+
+ break;
+#ifndef CLIENT_DLL
+ case CHARGER_STATE_START_CHARGE:
+ {
+ if ( m_hChargerSprite == NULL )
+ break;
+
+ m_hChargerSprite->SetBrightness( 32, 0.5f );
+ m_hChargerSprite->SetScale( 0.025f, 0.5f );
+ m_hChargerSprite->TurnOn();
+ }
+
+ break;
+
+ case CHARGER_STATE_READY:
+ {
+ // Get fully charged
+ if ( m_hChargerSprite == NULL )
+ break;
+
+ m_hChargerSprite->SetBrightness( 80, 1.0f );
+ m_hChargerSprite->SetScale( 0.1f, 0.5f );
+ m_hChargerSprite->TurnOn();
+ }
+
+ break;
+
+ case CHARGER_STATE_DISCHARGE:
+ {
+ SetSkin( BOLT_SKIN_NORMAL );
+
+ if ( m_hChargerSprite == NULL )
+ break;
+
+ m_hChargerSprite->SetBrightness( 0 );
+ m_hChargerSprite->TurnOff();
+ }
+
+ break;
+#endif
+ case CHARGER_STATE_OFF:
+ {
+ SetSkin( BOLT_SKIN_NORMAL );
+
+#ifndef CLIENT_DLL
+ if ( m_hChargerSprite == NULL )
+ break;
+
+ m_hChargerSprite->SetBrightness( 0 );
+ m_hChargerSprite->TurnOff();
+#endif
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+#ifndef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEvent -
+// *pOperator -
+//-----------------------------------------------------------------------------
+void CWeaponCrossbow::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
+{
+ switch( pEvent->event )
+ {
+ case EVENT_WEAPON_THROW:
+ SetChargerState( CHARGER_STATE_START_LOAD );
+ break;
+
+ case EVENT_WEAPON_THROW2:
+ SetChargerState( CHARGER_STATE_START_CHARGE );
+ break;
+
+ case EVENT_WEAPON_THROW3:
+ SetChargerState( CHARGER_STATE_READY );
+ break;
+
+ 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
+//-----------------------------------------------------------------------------
+bool CWeaponCrossbow::SendWeaponAnim( int iActivity )
+{
+ int newActivity = iActivity;
+
+ // The last shot needs a non-loaded activity
+ if ( ( newActivity == ACT_VM_IDLE ) && ( m_iClip1 <= 0 ) )
+ {
+ newActivity = ACT_VM_FIDGET;
+ }
+
+ //For now, just set the ideal activity and be done with it
+ return BaseClass::SendWeaponAnim( newActivity );
+}
diff --git a/mp/src/game/shared/hl2mp/weapon_crowbar.cpp b/mp/src/game/shared/hl2mp/weapon_crowbar.cpp
index 800e1036..55a03d1d 100644
--- a/mp/src/game/shared/hl2mp/weapon_crowbar.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_crowbar.cpp
@@ -1,228 +1,228 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Crowbar - an old favorite
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "hl2mp/weapon_crowbar.h"
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-#include "gamerules.h"
-#include "ammodef.h"
-#include "mathlib/mathlib.h"
-#include "in_buttons.h"
-#include "vstdlib/random.h"
-#include "npcevent.h"
-
-#if defined( CLIENT_DLL )
- #include "c_hl2mp_player.h"
-#else
- #include "hl2mp_player.h"
- #include "ai_basenpc.h"
-#endif
-
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define CROWBAR_RANGE 75.0f
-#define CROWBAR_REFIRE 0.4f
-
-
-//-----------------------------------------------------------------------------
-// CWeaponCrowbar
-//-----------------------------------------------------------------------------
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrowbar, DT_WeaponCrowbar )
-
-BEGIN_NETWORK_TABLE( CWeaponCrowbar, DT_WeaponCrowbar )
-END_NETWORK_TABLE()
-
-BEGIN_PREDICTION_DATA( CWeaponCrowbar )
-END_PREDICTION_DATA()
-
-LINK_ENTITY_TO_CLASS( weapon_crowbar, CWeaponCrowbar );
-PRECACHE_WEAPON_REGISTER( weapon_crowbar );
-
-#ifndef CLIENT_DLL
-
-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 },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponCrowbar);
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CWeaponCrowbar::CWeaponCrowbar( void )
-{
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the damage amount for the animation we're doing
-// Input : hitActivity - currently played activity
-// Output : Damage amount
-//-----------------------------------------------------------------------------
-float CWeaponCrowbar::GetDamageForActivity( Activity hitActivity )
-{
- return 25.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Add in a view kick for this weapon
-//-----------------------------------------------------------------------------
-void CWeaponCrowbar::AddViewKick( void )
-{
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if ( pPlayer == NULL )
- return;
-
- QAngle punchAng;
-
- punchAng.x = SharedRandomFloat( "crowbarpax", 1.0f, 2.0f );
- punchAng.y = SharedRandomFloat( "crowbarpay", -2.0f, -1.0f );
- punchAng.z = 0.0f;
-
- pPlayer->ViewPunch( punchAng );
-}
-
-
-#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!)
-//-----------------------------------------------------------------------------
-ConVar sk_crowbar_lead_time( "sk_crowbar_lead_time", "0.9" );
-
-int CWeaponCrowbar::WeaponMeleeAttack1Condition( float flDot, float flDist )
-{
- // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
- CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
- CBaseEntity *pEnemy = pNPC->GetEnemy();
- if (!pEnemy)
- return COND_NONE;
-
- Vector vecVelocity;
- vecVelocity = pEnemy->GetSmoothedVelocity( );
-
- // Project where the enemy will be in a little while
- float dt = sk_crowbar_lead_time.GetFloat();
- dt += SharedRandomFloat( "crowbarmelee1", -0.3f, 0.2f );
- if ( dt < 0.0f )
- dt = 0.0f;
-
- Vector vecExtrapolatedPos;
- VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos );
-
- Vector vecDelta;
- VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta );
-
- if ( fabs( vecDelta.z ) > 70 )
- {
- return COND_TOO_FAR_TO_ATTACK;
- }
-
- Vector vecForward = pNPC->BodyDirection2D( );
- vecDelta.z = 0.0f;
- float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() );
- if ((flDist > 64) && (flExtrapolatedDist > 64))
- {
- return COND_TOO_FAR_TO_ATTACK;
- }
-
- float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() );
- if ((flDot < 0.7) && (flExtrapolatedDot < 0.7))
- {
- return COND_NOT_FACING_ATTACK;
- }
-
- return COND_CAN_MELEE_ATTACK1;
-}
-
-#endif
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponCrowbar::Drop( const Vector &vecVelocity )
-{
-#ifndef CLIENT_DLL
- UTIL_Remove( this );
-#endif
-}
-
-float CWeaponCrowbar::GetRange( void )
-{
- return CROWBAR_RANGE;
-}
-
-float CWeaponCrowbar::GetFireRate( void )
-{
- return CROWBAR_REFIRE;
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Crowbar - an old favorite
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "hl2mp/weapon_crowbar.h"
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+#include "gamerules.h"
+#include "ammodef.h"
+#include "mathlib/mathlib.h"
+#include "in_buttons.h"
+#include "vstdlib/random.h"
+#include "npcevent.h"
+
+#if defined( CLIENT_DLL )
+ #include "c_hl2mp_player.h"
+#else
+ #include "hl2mp_player.h"
+ #include "ai_basenpc.h"
+#endif
+
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define CROWBAR_RANGE 75.0f
+#define CROWBAR_REFIRE 0.4f
+
+
+//-----------------------------------------------------------------------------
+// CWeaponCrowbar
+//-----------------------------------------------------------------------------
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCrowbar, DT_WeaponCrowbar )
+
+BEGIN_NETWORK_TABLE( CWeaponCrowbar, DT_WeaponCrowbar )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCrowbar )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_crowbar, CWeaponCrowbar );
+PRECACHE_WEAPON_REGISTER( weapon_crowbar );
+
+#ifndef CLIENT_DLL
+
+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 },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponCrowbar);
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CWeaponCrowbar::CWeaponCrowbar( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the damage amount for the animation we're doing
+// Input : hitActivity - currently played activity
+// Output : Damage amount
+//-----------------------------------------------------------------------------
+float CWeaponCrowbar::GetDamageForActivity( Activity hitActivity )
+{
+ return 25.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add in a view kick for this weapon
+//-----------------------------------------------------------------------------
+void CWeaponCrowbar::AddViewKick( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if ( pPlayer == NULL )
+ return;
+
+ QAngle punchAng;
+
+ punchAng.x = SharedRandomFloat( "crowbarpax", 1.0f, 2.0f );
+ punchAng.y = SharedRandomFloat( "crowbarpay", -2.0f, -1.0f );
+ punchAng.z = 0.0f;
+
+ pPlayer->ViewPunch( punchAng );
+}
+
+
+#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!)
+//-----------------------------------------------------------------------------
+ConVar sk_crowbar_lead_time( "sk_crowbar_lead_time", "0.9" );
+
+int CWeaponCrowbar::WeaponMeleeAttack1Condition( float flDot, float flDist )
+{
+ // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
+ CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
+ CBaseEntity *pEnemy = pNPC->GetEnemy();
+ if (!pEnemy)
+ return COND_NONE;
+
+ Vector vecVelocity;
+ vecVelocity = pEnemy->GetSmoothedVelocity( );
+
+ // Project where the enemy will be in a little while
+ float dt = sk_crowbar_lead_time.GetFloat();
+ dt += SharedRandomFloat( "crowbarmelee1", -0.3f, 0.2f );
+ if ( dt < 0.0f )
+ dt = 0.0f;
+
+ Vector vecExtrapolatedPos;
+ VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos );
+
+ Vector vecDelta;
+ VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta );
+
+ if ( fabs( vecDelta.z ) > 70 )
+ {
+ return COND_TOO_FAR_TO_ATTACK;
+ }
+
+ Vector vecForward = pNPC->BodyDirection2D( );
+ vecDelta.z = 0.0f;
+ float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() );
+ if ((flDist > 64) && (flExtrapolatedDist > 64))
+ {
+ return COND_TOO_FAR_TO_ATTACK;
+ }
+
+ float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() );
+ if ((flDot < 0.7) && (flExtrapolatedDot < 0.7))
+ {
+ return COND_NOT_FACING_ATTACK;
+ }
+
+ return COND_CAN_MELEE_ATTACK1;
+}
+
+#endif
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCrowbar::Drop( const Vector &vecVelocity )
+{
+#ifndef CLIENT_DLL
+ UTIL_Remove( this );
+#endif
+}
+
+float CWeaponCrowbar::GetRange( void )
+{
+ return CROWBAR_RANGE;
+}
+
+float CWeaponCrowbar::GetFireRate( void )
+{
+ return CROWBAR_REFIRE;
+}
+
+
diff --git a/mp/src/game/shared/hl2mp/weapon_crowbar.h b/mp/src/game/shared/hl2mp/weapon_crowbar.h
index ad62f37a..2cd10fb9 100644
--- a/mp/src/game/shared/hl2mp/weapon_crowbar.h
+++ b/mp/src/game/shared/hl2mp/weapon_crowbar.h
@@ -1,70 +1,70 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $Workfile: $
-// $Date: $
-//
-//-----------------------------------------------------------------------------
-// $Log: $
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef HL2MP_WEAPON_CROWBAR_H
-#define HL2MP_WEAPON_CROWBAR_H
-#pragma once
-
-
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-#include "weapon_hl2mpbasebasebludgeon.h"
-
-
-#ifdef CLIENT_DLL
-#define CWeaponCrowbar C_WeaponCrowbar
-#endif
-
-//-----------------------------------------------------------------------------
-// CWeaponCrowbar
-//-----------------------------------------------------------------------------
-
-class CWeaponCrowbar : public CBaseHL2MPBludgeonWeapon
-{
-public:
- DECLARE_CLASS( CWeaponCrowbar, CBaseHL2MPBludgeonWeapon );
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-
- CWeaponCrowbar();
-
- float GetRange( void );
- float GetFireRate( void );
-
- void AddViewKick( void );
- float GetDamageForActivity( Activity hitActivity );
- void SecondaryAttack( void ) { return; }
-
- void Drop( const Vector &vecVelocity );
-
-
- // Animation event
-#ifndef CLIENT_DLL
- virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
- void HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
- int WeaponMeleeAttack1Condition( float flDot, float flDist );
-#endif
-
- CWeaponCrowbar( const CWeaponCrowbar & );
-
-private:
-
-};
-
-
-#endif // HL2MP_WEAPON_CROWBAR_H
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef HL2MP_WEAPON_CROWBAR_H
+#define HL2MP_WEAPON_CROWBAR_H
+#pragma once
+
+
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+#include "weapon_hl2mpbasebasebludgeon.h"
+
+
+#ifdef CLIENT_DLL
+#define CWeaponCrowbar C_WeaponCrowbar
+#endif
+
+//-----------------------------------------------------------------------------
+// CWeaponCrowbar
+//-----------------------------------------------------------------------------
+
+class CWeaponCrowbar : public CBaseHL2MPBludgeonWeapon
+{
+public:
+ DECLARE_CLASS( CWeaponCrowbar, CBaseHL2MPBludgeonWeapon );
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+
+ CWeaponCrowbar();
+
+ float GetRange( void );
+ float GetFireRate( void );
+
+ void AddViewKick( void );
+ float GetDamageForActivity( Activity hitActivity );
+ void SecondaryAttack( void ) { return; }
+
+ void Drop( const Vector &vecVelocity );
+
+
+ // Animation event
+#ifndef CLIENT_DLL
+ virtual void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
+ void HandleAnimEventMeleeHit( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
+ int WeaponMeleeAttack1Condition( float flDot, float flDist );
+#endif
+
+ CWeaponCrowbar( const CWeaponCrowbar & );
+
+private:
+
+};
+
+
+#endif // HL2MP_WEAPON_CROWBAR_H
+
diff --git a/mp/src/game/shared/hl2mp/weapon_frag.cpp b/mp/src/game/shared/hl2mp/weapon_frag.cpp
index 9319b869..9ddcb813 100644
--- a/mp/src/game/shared/hl2mp/weapon_frag.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_frag.cpp
@@ -1,552 +1,552 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "in_buttons.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
- #include "c_te_effect_dispatch.h"
-#else
- #include "hl2mp_player.h"
- #include "te_effect_dispatch.h"
- #include "grenade_frag.h"
-#endif
-
-#include "weapon_ar2.h"
-#include "effect_dispatch_data.h"
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define GRENADE_TIMER 2.5f //Seconds
-
-#define GRENADE_PAUSED_NO 0
-#define GRENADE_PAUSED_PRIMARY 1
-#define GRENADE_PAUSED_SECONDARY 2
-
-#define GRENADE_RADIUS 4.0f // inches
-
-#define GRENADE_DAMAGE_RADIUS 250.0f
-
-#ifdef CLIENT_DLL
-#define CWeaponFrag C_WeaponFrag
-#endif
-
-//-----------------------------------------------------------------------------
-// Fragmentation grenades
-//-----------------------------------------------------------------------------
-class CWeaponFrag: public CBaseHL2MPCombatWeapon
-{
- DECLARE_CLASS( CWeaponFrag, CBaseHL2MPCombatWeapon );
-public:
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- CWeaponFrag();
-
- void Precache( void );
- void PrimaryAttack( void );
- void SecondaryAttack( void );
- void DecrementAmmo( CBaseCombatCharacter *pOwner );
- void ItemPostFrame( void );
-
- bool Deploy( void );
- bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
-
- 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 ); }
-
-private:
-
- void RollGrenade( CBasePlayer *pPlayer );
- void LobGrenade( CBasePlayer *pPlayer );
- // check a throw from vecSrc. If not valid, move the position back along the line to vecEye
- void CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc );
-
- CNetworkVar( bool, m_bRedraw ); //Draw the weapon again after throwing a grenade
-
- CNetworkVar( int, m_AttackPaused );
- CNetworkVar( bool, m_fDrawbackFinished );
-
- CWeaponFrag( const CWeaponFrag & );
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-};
-
-#ifndef CLIENT_DLL
-
-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 },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponFrag);
-
-#endif
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFrag, DT_WeaponFrag )
-
-BEGIN_NETWORK_TABLE( CWeaponFrag, DT_WeaponFrag )
-
-#ifdef CLIENT_DLL
- RecvPropBool( RECVINFO( m_bRedraw ) ),
- RecvPropBool( RECVINFO( m_fDrawbackFinished ) ),
- RecvPropInt( RECVINFO( m_AttackPaused ) ),
-#else
- SendPropBool( SENDINFO( m_bRedraw ) ),
- SendPropBool( SENDINFO( m_fDrawbackFinished ) ),
- SendPropInt( SENDINFO( m_AttackPaused ) ),
-#endif
-
-END_NETWORK_TABLE()
-
-#ifdef CLIENT_DLL
-BEGIN_PREDICTION_DATA( CWeaponFrag )
- DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_AttackPaused, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
-END_PREDICTION_DATA()
-#endif
-
-LINK_ENTITY_TO_CLASS( weapon_frag, CWeaponFrag );
-PRECACHE_WEAPON_REGISTER(weapon_frag);
-
-CWeaponFrag::CWeaponFrag( void ) :
- CBaseHL2MPCombatWeapon()
-{
- m_bRedraw = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponFrag::Precache( void )
-{
- BaseClass::Precache();
-
-#ifndef CLIENT_DLL
- UTIL_PrecacheOther( "npc_grenade_frag" );
-#endif
-
- PrecacheScriptSound( "WeaponFrag.Throw" );
- PrecacheScriptSound( "WeaponFrag.Roll" );
-}
-
-#ifndef CLIENT_DLL
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEvent -
-// *pOperator -
-//-----------------------------------------------------------------------------
-void CWeaponFrag::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- bool fThrewGrenade = false;
-
- switch( pEvent->event )
- {
- case EVENT_WEAPON_SEQUENCE_FINISHED:
- m_fDrawbackFinished = true;
- break;
-
- case EVENT_WEAPON_THROW:
- ThrowGrenade( pOwner );
- DecrementAmmo( pOwner );
- fThrewGrenade = true;
- break;
-
- case EVENT_WEAPON_THROW2:
- RollGrenade( pOwner );
- DecrementAmmo( pOwner );
- fThrewGrenade = true;
- break;
-
- case EVENT_WEAPON_THROW3:
- LobGrenade( pOwner );
- DecrementAmmo( pOwner );
- fThrewGrenade = true;
- break;
-
- default:
- BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
- break;
- }
-
-#define RETHROW_DELAY 0.5
- if( fThrewGrenade )
- {
- 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!
- }
-}
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CWeaponFrag::Deploy( void )
-{
- m_bRedraw = false;
- m_fDrawbackFinished = false;
-
- return BaseClass::Deploy();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponFrag::Holster( CBaseCombatWeapon *pSwitchingTo )
-{
- m_bRedraw = false;
- m_fDrawbackFinished = false;
-
- return BaseClass::Holster( pSwitchingTo );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponFrag::Reload( void )
-{
- if ( !HasPrimaryAmmo() )
- return false;
-
- if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
- {
- //Redraw the weapon
- SendWeaponAnim( ACT_VM_DRAW );
-
- //Update our times
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
- m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
-
- //Mark this as done
- m_bRedraw = false;
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponFrag::SecondaryAttack( void )
-{
- if ( m_bRedraw )
- return;
-
- if ( !HasPrimaryAmmo() )
- return;
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- CBasePlayer *pPlayer = ToBasePlayer( pOwner );
-
- if ( pPlayer == NULL )
- return;
-
- // Note that this is a secondary attack and prepare the grenade attack to pause.
- m_AttackPaused = GRENADE_PAUSED_SECONDARY;
- SendWeaponAnim( ACT_VM_PULLBACK_LOW );
-
- // Don't let weapon idle interfere in the middle of a throw!
- m_flTimeWeaponIdle = FLT_MAX;
- m_flNextSecondaryAttack = FLT_MAX;
-
- // If I'm now out of ammo, switch away
- if ( !HasPrimaryAmmo() )
- {
- pPlayer->SwitchToNextBestWeapon( this );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponFrag::PrimaryAttack( void )
-{
- if ( m_bRedraw )
- return;
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- {
- return;
- }
-
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );;
-
- if ( !pPlayer )
- return;
-
- // Note that this is a primary attack and prepare the grenade attack to pause.
- m_AttackPaused = GRENADE_PAUSED_PRIMARY;
- SendWeaponAnim( ACT_VM_PULLBACK_HIGH );
-
- // Put both of these off indefinitely. We do not know how long
- // the player will hold the grenade.
- m_flTimeWeaponIdle = FLT_MAX;
- m_flNextPrimaryAttack = FLT_MAX;
-
- // If I'm now out of ammo, switch away
- if ( !HasPrimaryAmmo() )
- {
- pPlayer->SwitchToNextBestWeapon( this );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pOwner -
-//-----------------------------------------------------------------------------
-void CWeaponFrag::DecrementAmmo( CBaseCombatCharacter *pOwner )
-{
- pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponFrag::ItemPostFrame( void )
-{
- if( m_fDrawbackFinished )
- {
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if (pOwner)
- {
- switch( m_AttackPaused )
- {
- case GRENADE_PAUSED_PRIMARY:
- if( !(pOwner->m_nButtons & IN_ATTACK) )
- {
- SendWeaponAnim( ACT_VM_THROW );
- m_fDrawbackFinished = false;
- }
- break;
-
- case GRENADE_PAUSED_SECONDARY:
- if( !(pOwner->m_nButtons & IN_ATTACK2) )
- {
- //See if we're ducking
- if ( pOwner->m_nButtons & IN_DUCK )
- {
- //Send the weapon animation
- SendWeaponAnim( ACT_VM_SECONDARYATTACK );
- }
- else
- {
- //Send the weapon animation
- SendWeaponAnim( ACT_VM_HAULBACK );
- }
-
- m_fDrawbackFinished = false;
- }
- break;
-
- default:
- break;
- }
- }
- }
-
- BaseClass::ItemPostFrame();
-
- if ( m_bRedraw )
- {
- if ( IsViewModelSequenceFinished() )
- {
- Reload();
- }
- }
-}
-
- // check a throw from vecSrc. If not valid, move the position back along the line to vecEye
-void CWeaponFrag::CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc )
-{
- trace_t tr;
-
- UTIL_TraceHull( vecEye, vecSrc, -Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2), Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2),
- pPlayer->PhysicsSolidMaskForEntity(), pPlayer, pPlayer->GetCollisionGroup(), &tr );
-
- if ( tr.DidHit() )
- {
- vecSrc = tr.endpos;
- }
-}
-
-void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade )
-{
- CWeaponFrag *pWeaponFrag = dynamic_cast<CWeaponFrag*>( pGrenade );
-
- if ( pWeaponFrag )
- {
- pWeaponFrag->ThrowGrenade( pPlayer );
- pWeaponFrag->DecrementAmmo( pPlayer );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPlayer -
-//-----------------------------------------------------------------------------
-void CWeaponFrag::ThrowGrenade( CBasePlayer *pPlayer )
-{
-#ifndef CLIENT_DLL
- Vector vecEye = pPlayer->EyePosition();
- Vector vForward, vRight;
-
- pPlayer->EyeVectors( &vForward, &vRight, NULL );
- Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f;
- CheckThrowPosition( pPlayer, vecEye, vecSrc );
-// vForward[0] += 0.1f;
- vForward[2] += 0.1f;
-
- Vector vecThrow;
- pPlayer->GetVelocity( &vecThrow, NULL );
- vecThrow += vForward * 1200;
- CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, GRENADE_TIMER, false );
-
- if ( pGrenade )
- {
- if ( pPlayer && pPlayer->m_lifeState != LIFE_ALIVE )
- {
- pPlayer->GetVelocity( &vecThrow, NULL );
-
- IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
- if ( pPhysicsObject )
- {
- pPhysicsObject->SetVelocity( &vecThrow, NULL );
- }
- }
-
- pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
- pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
- }
-#endif
-
- m_bRedraw = true;
-
- WeaponSound( SINGLE );
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPlayer -
-//-----------------------------------------------------------------------------
-void CWeaponFrag::LobGrenade( CBasePlayer *pPlayer )
-{
-#ifndef CLIENT_DLL
- Vector vecEye = pPlayer->EyePosition();
- Vector vForward, vRight;
-
- pPlayer->EyeVectors( &vForward, &vRight, NULL );
- Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f + Vector( 0, 0, -8 );
- CheckThrowPosition( pPlayer, vecEye, vecSrc );
-
- Vector vecThrow;
- pPlayer->GetVelocity( &vecThrow, NULL );
- vecThrow += vForward * 350 + Vector( 0, 0, 50 );
- CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(200,random->RandomInt(-600,600),0), pPlayer, GRENADE_TIMER, false );
-
- if ( pGrenade )
- {
- pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
- pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
- }
-#endif
-
- WeaponSound( WPN_DOUBLE );
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- m_bRedraw = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPlayer -
-//-----------------------------------------------------------------------------
-void CWeaponFrag::RollGrenade( CBasePlayer *pPlayer )
-{
-#ifndef CLIENT_DLL
- // BUGBUG: Hardcoded grenade width of 4 - better not change the model :)
- Vector vecSrc;
- pPlayer->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecSrc );
- vecSrc.z += GRENADE_RADIUS;
-
- Vector vecFacing = pPlayer->BodyDirection2D( );
- // no up/down direction
- vecFacing.z = 0;
- VectorNormalize( vecFacing );
- trace_t tr;
- UTIL_TraceLine( vecSrc, vecSrc - Vector(0,0,16), MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
- if ( tr.fraction != 1.0 )
- {
- // compute forward vec parallel to floor plane and roll grenade along that
- Vector tangent;
- CrossProduct( vecFacing, tr.plane.normal, tangent );
- CrossProduct( tr.plane.normal, tangent, vecFacing );
- }
- vecSrc += (vecFacing * 18.0);
- CheckThrowPosition( pPlayer, pPlayer->WorldSpaceCenter(), vecSrc );
-
- Vector vecThrow;
- pPlayer->GetVelocity( &vecThrow, NULL );
- vecThrow += vecFacing * 700;
- // put it on its side
- QAngle orientation(0,pPlayer->GetLocalAngles().y,-90);
- // roll it
- AngularImpulse rotSpeed(0,0,720);
- CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, orientation, vecThrow, rotSpeed, pPlayer, GRENADE_TIMER, false );
-
- if ( pGrenade )
- {
- pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
- pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
- }
-
-#endif
-
- WeaponSound( SPECIAL1 );
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- m_bRedraw = true;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+ #include "c_te_effect_dispatch.h"
+#else
+ #include "hl2mp_player.h"
+ #include "te_effect_dispatch.h"
+ #include "grenade_frag.h"
+#endif
+
+#include "weapon_ar2.h"
+#include "effect_dispatch_data.h"
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define GRENADE_TIMER 2.5f //Seconds
+
+#define GRENADE_PAUSED_NO 0
+#define GRENADE_PAUSED_PRIMARY 1
+#define GRENADE_PAUSED_SECONDARY 2
+
+#define GRENADE_RADIUS 4.0f // inches
+
+#define GRENADE_DAMAGE_RADIUS 250.0f
+
+#ifdef CLIENT_DLL
+#define CWeaponFrag C_WeaponFrag
+#endif
+
+//-----------------------------------------------------------------------------
+// Fragmentation grenades
+//-----------------------------------------------------------------------------
+class CWeaponFrag: public CBaseHL2MPCombatWeapon
+{
+ DECLARE_CLASS( CWeaponFrag, CBaseHL2MPCombatWeapon );
+public:
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponFrag();
+
+ void Precache( void );
+ void PrimaryAttack( void );
+ void SecondaryAttack( void );
+ void DecrementAmmo( CBaseCombatCharacter *pOwner );
+ void ItemPostFrame( void );
+
+ bool Deploy( void );
+ bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+
+ 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 ); }
+
+private:
+
+ void RollGrenade( CBasePlayer *pPlayer );
+ void LobGrenade( CBasePlayer *pPlayer );
+ // check a throw from vecSrc. If not valid, move the position back along the line to vecEye
+ void CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc );
+
+ CNetworkVar( bool, m_bRedraw ); //Draw the weapon again after throwing a grenade
+
+ CNetworkVar( int, m_AttackPaused );
+ CNetworkVar( bool, m_fDrawbackFinished );
+
+ CWeaponFrag( const CWeaponFrag & );
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+};
+
+#ifndef CLIENT_DLL
+
+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 },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponFrag);
+
+#endif
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFrag, DT_WeaponFrag )
+
+BEGIN_NETWORK_TABLE( CWeaponFrag, DT_WeaponFrag )
+
+#ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bRedraw ) ),
+ RecvPropBool( RECVINFO( m_fDrawbackFinished ) ),
+ RecvPropInt( RECVINFO( m_AttackPaused ) ),
+#else
+ SendPropBool( SENDINFO( m_bRedraw ) ),
+ SendPropBool( SENDINFO( m_fDrawbackFinished ) ),
+ SendPropInt( SENDINFO( m_AttackPaused ) ),
+#endif
+
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponFrag )
+ DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_fDrawbackFinished, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_AttackPaused, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_frag, CWeaponFrag );
+PRECACHE_WEAPON_REGISTER(weapon_frag);
+
+CWeaponFrag::CWeaponFrag( void ) :
+ CBaseHL2MPCombatWeapon()
+{
+ m_bRedraw = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponFrag::Precache( void )
+{
+ BaseClass::Precache();
+
+#ifndef CLIENT_DLL
+ UTIL_PrecacheOther( "npc_grenade_frag" );
+#endif
+
+ PrecacheScriptSound( "WeaponFrag.Throw" );
+ PrecacheScriptSound( "WeaponFrag.Roll" );
+}
+
+#ifndef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEvent -
+// *pOperator -
+//-----------------------------------------------------------------------------
+void CWeaponFrag::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ bool fThrewGrenade = false;
+
+ switch( pEvent->event )
+ {
+ case EVENT_WEAPON_SEQUENCE_FINISHED:
+ m_fDrawbackFinished = true;
+ break;
+
+ case EVENT_WEAPON_THROW:
+ ThrowGrenade( pOwner );
+ DecrementAmmo( pOwner );
+ fThrewGrenade = true;
+ break;
+
+ case EVENT_WEAPON_THROW2:
+ RollGrenade( pOwner );
+ DecrementAmmo( pOwner );
+ fThrewGrenade = true;
+ break;
+
+ case EVENT_WEAPON_THROW3:
+ LobGrenade( pOwner );
+ DecrementAmmo( pOwner );
+ fThrewGrenade = true;
+ break;
+
+ default:
+ BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
+ break;
+ }
+
+#define RETHROW_DELAY 0.5
+ if( fThrewGrenade )
+ {
+ 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!
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponFrag::Deploy( void )
+{
+ m_bRedraw = false;
+ m_fDrawbackFinished = false;
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponFrag::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ m_bRedraw = false;
+ m_fDrawbackFinished = false;
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponFrag::Reload( void )
+{
+ if ( !HasPrimaryAmmo() )
+ return false;
+
+ if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
+ {
+ //Redraw the weapon
+ SendWeaponAnim( ACT_VM_DRAW );
+
+ //Update our times
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
+ m_flTimeWeaponIdle = gpGlobals->curtime + SequenceDuration();
+
+ //Mark this as done
+ m_bRedraw = false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponFrag::SecondaryAttack( void )
+{
+ if ( m_bRedraw )
+ return;
+
+ if ( !HasPrimaryAmmo() )
+ return;
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ CBasePlayer *pPlayer = ToBasePlayer( pOwner );
+
+ if ( pPlayer == NULL )
+ return;
+
+ // Note that this is a secondary attack and prepare the grenade attack to pause.
+ m_AttackPaused = GRENADE_PAUSED_SECONDARY;
+ SendWeaponAnim( ACT_VM_PULLBACK_LOW );
+
+ // Don't let weapon idle interfere in the middle of a throw!
+ m_flTimeWeaponIdle = FLT_MAX;
+ m_flNextSecondaryAttack = FLT_MAX;
+
+ // If I'm now out of ammo, switch away
+ if ( !HasPrimaryAmmo() )
+ {
+ pPlayer->SwitchToNextBestWeapon( this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponFrag::PrimaryAttack( void )
+{
+ if ( m_bRedraw )
+ return;
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ {
+ return;
+ }
+
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );;
+
+ if ( !pPlayer )
+ return;
+
+ // Note that this is a primary attack and prepare the grenade attack to pause.
+ m_AttackPaused = GRENADE_PAUSED_PRIMARY;
+ SendWeaponAnim( ACT_VM_PULLBACK_HIGH );
+
+ // Put both of these off indefinitely. We do not know how long
+ // the player will hold the grenade.
+ m_flTimeWeaponIdle = FLT_MAX;
+ m_flNextPrimaryAttack = FLT_MAX;
+
+ // If I'm now out of ammo, switch away
+ if ( !HasPrimaryAmmo() )
+ {
+ pPlayer->SwitchToNextBestWeapon( this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOwner -
+//-----------------------------------------------------------------------------
+void CWeaponFrag::DecrementAmmo( CBaseCombatCharacter *pOwner )
+{
+ pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponFrag::ItemPostFrame( void )
+{
+ if( m_fDrawbackFinished )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if (pOwner)
+ {
+ switch( m_AttackPaused )
+ {
+ case GRENADE_PAUSED_PRIMARY:
+ if( !(pOwner->m_nButtons & IN_ATTACK) )
+ {
+ SendWeaponAnim( ACT_VM_THROW );
+ m_fDrawbackFinished = false;
+ }
+ break;
+
+ case GRENADE_PAUSED_SECONDARY:
+ if( !(pOwner->m_nButtons & IN_ATTACK2) )
+ {
+ //See if we're ducking
+ if ( pOwner->m_nButtons & IN_DUCK )
+ {
+ //Send the weapon animation
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+ }
+ else
+ {
+ //Send the weapon animation
+ SendWeaponAnim( ACT_VM_HAULBACK );
+ }
+
+ m_fDrawbackFinished = false;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ BaseClass::ItemPostFrame();
+
+ if ( m_bRedraw )
+ {
+ if ( IsViewModelSequenceFinished() )
+ {
+ Reload();
+ }
+ }
+}
+
+ // check a throw from vecSrc. If not valid, move the position back along the line to vecEye
+void CWeaponFrag::CheckThrowPosition( CBasePlayer *pPlayer, const Vector &vecEye, Vector &vecSrc )
+{
+ trace_t tr;
+
+ UTIL_TraceHull( vecEye, vecSrc, -Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2), Vector(GRENADE_RADIUS+2,GRENADE_RADIUS+2,GRENADE_RADIUS+2),
+ pPlayer->PhysicsSolidMaskForEntity(), pPlayer, pPlayer->GetCollisionGroup(), &tr );
+
+ if ( tr.DidHit() )
+ {
+ vecSrc = tr.endpos;
+ }
+}
+
+void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade )
+{
+ CWeaponFrag *pWeaponFrag = dynamic_cast<CWeaponFrag*>( pGrenade );
+
+ if ( pWeaponFrag )
+ {
+ pWeaponFrag->ThrowGrenade( pPlayer );
+ pWeaponFrag->DecrementAmmo( pPlayer );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+//-----------------------------------------------------------------------------
+void CWeaponFrag::ThrowGrenade( CBasePlayer *pPlayer )
+{
+#ifndef CLIENT_DLL
+ Vector vecEye = pPlayer->EyePosition();
+ Vector vForward, vRight;
+
+ pPlayer->EyeVectors( &vForward, &vRight, NULL );
+ Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f;
+ CheckThrowPosition( pPlayer, vecEye, vecSrc );
+// vForward[0] += 0.1f;
+ vForward[2] += 0.1f;
+
+ Vector vecThrow;
+ pPlayer->GetVelocity( &vecThrow, NULL );
+ vecThrow += vForward * 1200;
+ CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer, GRENADE_TIMER, false );
+
+ if ( pGrenade )
+ {
+ if ( pPlayer && pPlayer->m_lifeState != LIFE_ALIVE )
+ {
+ pPlayer->GetVelocity( &vecThrow, NULL );
+
+ IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->SetVelocity( &vecThrow, NULL );
+ }
+ }
+
+ pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
+ pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
+ }
+#endif
+
+ m_bRedraw = true;
+
+ WeaponSound( SINGLE );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+//-----------------------------------------------------------------------------
+void CWeaponFrag::LobGrenade( CBasePlayer *pPlayer )
+{
+#ifndef CLIENT_DLL
+ Vector vecEye = pPlayer->EyePosition();
+ Vector vForward, vRight;
+
+ pPlayer->EyeVectors( &vForward, &vRight, NULL );
+ Vector vecSrc = vecEye + vForward * 18.0f + vRight * 8.0f + Vector( 0, 0, -8 );
+ CheckThrowPosition( pPlayer, vecEye, vecSrc );
+
+ Vector vecThrow;
+ pPlayer->GetVelocity( &vecThrow, NULL );
+ vecThrow += vForward * 350 + Vector( 0, 0, 50 );
+ CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, vec3_angle, vecThrow, AngularImpulse(200,random->RandomInt(-600,600),0), pPlayer, GRENADE_TIMER, false );
+
+ if ( pGrenade )
+ {
+ pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
+ pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
+ }
+#endif
+
+ WeaponSound( WPN_DOUBLE );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ m_bRedraw = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+//-----------------------------------------------------------------------------
+void CWeaponFrag::RollGrenade( CBasePlayer *pPlayer )
+{
+#ifndef CLIENT_DLL
+ // BUGBUG: Hardcoded grenade width of 4 - better not change the model :)
+ Vector vecSrc;
+ pPlayer->CollisionProp()->NormalizedToWorldSpace( Vector( 0.5f, 0.5f, 0.0f ), &vecSrc );
+ vecSrc.z += GRENADE_RADIUS;
+
+ Vector vecFacing = pPlayer->BodyDirection2D( );
+ // no up/down direction
+ vecFacing.z = 0;
+ VectorNormalize( vecFacing );
+ trace_t tr;
+ UTIL_TraceLine( vecSrc, vecSrc - Vector(0,0,16), MASK_PLAYERSOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction != 1.0 )
+ {
+ // compute forward vec parallel to floor plane and roll grenade along that
+ Vector tangent;
+ CrossProduct( vecFacing, tr.plane.normal, tangent );
+ CrossProduct( tr.plane.normal, tangent, vecFacing );
+ }
+ vecSrc += (vecFacing * 18.0);
+ CheckThrowPosition( pPlayer, pPlayer->WorldSpaceCenter(), vecSrc );
+
+ Vector vecThrow;
+ pPlayer->GetVelocity( &vecThrow, NULL );
+ vecThrow += vecFacing * 700;
+ // put it on its side
+ QAngle orientation(0,pPlayer->GetLocalAngles().y,-90);
+ // roll it
+ AngularImpulse rotSpeed(0,0,720);
+ CBaseGrenade *pGrenade = Fraggrenade_Create( vecSrc, orientation, vecThrow, rotSpeed, pPlayer, GRENADE_TIMER, false );
+
+ if ( pGrenade )
+ {
+ pGrenade->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
+ pGrenade->SetDamageRadius( GRENADE_DAMAGE_RADIUS );
+ }
+
+#endif
+
+ WeaponSound( SPECIAL1 );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ m_bRedraw = true;
+}
+
diff --git a/mp/src/game/shared/hl2mp/weapon_hl2mpbase.cpp b/mp/src/game/shared/hl2mp/weapon_hl2mpbase.cpp
index 1f54e84c..15995a5b 100644
--- a/mp/src/game/shared/hl2mp/weapon_hl2mpbase.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_hl2mpbase.cpp
@@ -1,330 +1,330 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "in_buttons.h"
-#include "takedamageinfo.h"
-#include "ammodef.h"
-#include "hl2mp_gamerules.h"
-
-
-#ifdef CLIENT_DLL
-extern IVModelInfoClient* modelinfo;
-#else
-extern IVModelInfo* modelinfo;
-#endif
-
-
-#if defined( CLIENT_DLL )
-
- #include "vgui/ISurface.h"
- #include "vgui_controls/Controls.h"
- #include "c_hl2mp_player.h"
- #include "hud_crosshair.h"
-
-#else
-
- #include "hl2mp_player.h"
- #include "vphysics/constraints.h"
-
-#endif
-
-#include "weapon_hl2mpbase.h"
-
-
-// ----------------------------------------------------------------------------- //
-// Global functions.
-// ----------------------------------------------------------------------------- //
-
-bool IsAmmoType( int iAmmoType, const char *pAmmoName )
-{
- return GetAmmoDef()->Index( pAmmoName ) == iAmmoType;
-}
-
-static const char * s_WeaponAliasInfo[] =
-{
- "none", // WEAPON_NONE = 0,
-
- //Melee
- "shotgun", //WEAPON_AMERKNIFE,
-
- NULL, // end of list marker
-};
-
-
-// ----------------------------------------------------------------------------- //
-// CWeaponHL2MPBase tables.
-// ----------------------------------------------------------------------------- //
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponHL2MPBase, DT_WeaponHL2MPBase )
-
-BEGIN_NETWORK_TABLE( CWeaponHL2MPBase, DT_WeaponHL2MPBase )
-
-#ifdef CLIENT_DLL
-
-#else
- // world weapon models have no aminations
- // SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ),
-// SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
-// SendPropExclude( "DT_LocalActiveWeaponData", "m_flTimeWeaponIdle" ),
-#endif
-
-END_NETWORK_TABLE()
-
-BEGIN_PREDICTION_DATA( CWeaponHL2MPBase )
-END_PREDICTION_DATA()
-
-LINK_ENTITY_TO_CLASS( weapon_hl2mp_base, CWeaponHL2MPBase );
-
-
-#ifdef GAME_DLL
-
- BEGIN_DATADESC( CWeaponHL2MPBase )
-
- END_DATADESC()
-
-#endif
-
-// ----------------------------------------------------------------------------- //
-// CWeaponHL2MPBase implementation.
-// ----------------------------------------------------------------------------- //
-CWeaponHL2MPBase::CWeaponHL2MPBase()
-{
- SetPredictionEligible( true );
- AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches.
-
- m_flNextResetCheckTime = 0.0f;
-}
-
-
-bool CWeaponHL2MPBase::IsPredicted() const
-{
- return true;
-}
-
-void CWeaponHL2MPBase::WeaponSound( WeaponSound_t sound_type, float soundtime /* = 0.0f */ )
-{
-#ifdef CLIENT_DLL
-
- // If we have some sounds from the weapon classname.txt file, play a random one of them
- const char *shootsound = GetWpnData().aShootSounds[ sound_type ];
- if ( !shootsound || !shootsound[0] )
- return;
-
- CBroadcastRecipientFilter filter; // this is client side only
- if ( !te->CanPredict() )
- return;
-
- CBaseEntity::EmitSound( filter, GetPlayerOwner()->entindex(), shootsound, &GetPlayerOwner()->GetAbsOrigin() );
-#else
- BaseClass::WeaponSound( sound_type, soundtime );
-#endif
-}
-
-
-CBasePlayer* CWeaponHL2MPBase::GetPlayerOwner() const
-{
- return dynamic_cast< CBasePlayer* >( GetOwner() );
-}
-
-CHL2MP_Player* CWeaponHL2MPBase::GetHL2MPPlayerOwner() const
-{
- return dynamic_cast< CHL2MP_Player* >( GetOwner() );
-}
-
-#ifdef CLIENT_DLL
-
-void CWeaponHL2MPBase::OnDataChanged( DataUpdateType_t type )
-{
- BaseClass::OnDataChanged( type );
-
- if ( GetPredictable() && !ShouldPredict() )
- ShutdownPredictable();
-}
-
-
-bool CWeaponHL2MPBase::ShouldPredict()
-{
- if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() )
- return true;
-
- return BaseClass::ShouldPredict();
-}
-
-
-#else
-
-void CWeaponHL2MPBase::Spawn()
-{
- BaseClass::Spawn();
-
- // Set this here to allow players to shoot dropped weapons
- SetCollisionGroup( COLLISION_GROUP_WEAPON );
-}
-
-void CWeaponHL2MPBase::Materialize( void )
-{
- if ( IsEffectActive( EF_NODRAW ) )
- {
- // changing from invisible state to visible.
- EmitSound( "AlyxEmp.Charge" );
-
- RemoveEffects( EF_NODRAW );
- DoMuzzleFlash();
- }
-
- if ( HasSpawnFlags( SF_NORESPAWN ) == false )
- {
- VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false );
- SetMoveType( MOVETYPE_VPHYSICS );
-
- HL2MPRules()->AddLevelDesignerPlacedObject( this );
- }
-
- if ( HasSpawnFlags( SF_NORESPAWN ) == false )
- {
- if ( GetOriginalSpawnOrigin() == vec3_origin )
- {
- m_vOriginalSpawnOrigin = GetAbsOrigin();
- m_vOriginalSpawnAngles = GetAbsAngles();
- }
- }
-
- SetPickupTouch();
-
- SetThink (NULL);
-}
-
-int CWeaponHL2MPBase::ObjectCaps()
-{
- return BaseClass::ObjectCaps() & ~FCAP_IMPULSE_USE;
-}
-
-#endif
-
-void CWeaponHL2MPBase::FallInit( void )
-{
-#ifndef CLIENT_DLL
- SetModel( GetWorldModel() );
- VPhysicsDestroyObject();
-
- if ( HasSpawnFlags( SF_NORESPAWN ) == false )
- {
- SetMoveType( MOVETYPE_NONE );
- SetSolid( SOLID_BBOX );
- AddSolidFlags( FSOLID_TRIGGER );
-
- UTIL_DropToFloor( this, MASK_SOLID );
- }
- else
- {
- if ( !VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ) )
- {
- SetMoveType( MOVETYPE_NONE );
- SetSolid( SOLID_BBOX );
- AddSolidFlags( FSOLID_TRIGGER );
- }
- else
- {
- #if !defined( CLIENT_DLL )
- // Constrained start?
- if ( HasSpawnFlags( SF_WEAPON_START_CONSTRAINED ) )
- {
- //Constrain the weapon in place
- IPhysicsObject *pReferenceObject, *pAttachedObject;
-
- pReferenceObject = g_PhysWorldObject;
- pAttachedObject = VPhysicsGetObject();
-
- if ( pReferenceObject && pAttachedObject )
- {
- constraint_fixedparams_t fixed;
- fixed.Defaults();
- fixed.InitWithCurrentObjectState( pReferenceObject, pAttachedObject );
-
- fixed.constraint.forceLimit = lbs2kg( 10000 );
- fixed.constraint.torqueLimit = lbs2kg( 10000 );
-
- IPhysicsConstraint *pConstraint = GetConstraint();
-
- pConstraint = physenv->CreateFixedConstraint( pReferenceObject, pAttachedObject, NULL, fixed );
-
- pConstraint->SetGameData( (void *) this );
- }
- }
- #endif //CLIENT_DLL
- }
- }
-
- SetPickupTouch();
-
- SetThink( &CBaseCombatWeapon::FallThink );
-
- SetNextThink( gpGlobals->curtime + 0.1f );
-
-#endif
-}
-
-const CHL2MPSWeaponInfo &CWeaponHL2MPBase::GetHL2MPWpnData() const
-{
- const FileWeaponInfo_t *pWeaponInfo = &GetWpnData();
- const CHL2MPSWeaponInfo *pHL2MPInfo;
-
- #ifdef _DEBUG
- pHL2MPInfo = dynamic_cast< const CHL2MPSWeaponInfo* >( pWeaponInfo );
- Assert( pHL2MPInfo );
- #else
- pHL2MPInfo = static_cast< const CHL2MPSWeaponInfo* >( pWeaponInfo );
- #endif
-
- return *pHL2MPInfo;
-}
-void CWeaponHL2MPBase::FireBullets( const FireBulletsInfo_t &info )
-{
- FireBulletsInfo_t modinfo = info;
-
- modinfo.m_iPlayerDamage = GetHL2MPWpnData().m_iPlayerDamage;
-
- BaseClass::FireBullets( modinfo );
-}
-
-
-#if defined( CLIENT_DLL )
-
-#include "c_te_effect_dispatch.h"
-
-#define NUM_MUZZLE_FLASH_TYPES 4
-
-bool CWeaponHL2MPBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
-{
- return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
-}
-
-
-void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip )
-{
- QAngle final = in + punch;
-
- //Clip each component
- for ( int i = 0; i < 3; i++ )
- {
- if ( final[i] > clip[i] )
- {
- final[i] = clip[i];
- }
- else if ( final[i] < -clip[i] )
- {
- final[i] = -clip[i];
- }
-
- //Return the result
- in[i] = final[i] - punch[i];
- }
-}
-
-#endif
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "in_buttons.h"
+#include "takedamageinfo.h"
+#include "ammodef.h"
+#include "hl2mp_gamerules.h"
+
+
+#ifdef CLIENT_DLL
+extern IVModelInfoClient* modelinfo;
+#else
+extern IVModelInfo* modelinfo;
+#endif
+
+
+#if defined( CLIENT_DLL )
+
+ #include "vgui/ISurface.h"
+ #include "vgui_controls/Controls.h"
+ #include "c_hl2mp_player.h"
+ #include "hud_crosshair.h"
+
+#else
+
+ #include "hl2mp_player.h"
+ #include "vphysics/constraints.h"
+
+#endif
+
+#include "weapon_hl2mpbase.h"
+
+
+// ----------------------------------------------------------------------------- //
+// Global functions.
+// ----------------------------------------------------------------------------- //
+
+bool IsAmmoType( int iAmmoType, const char *pAmmoName )
+{
+ return GetAmmoDef()->Index( pAmmoName ) == iAmmoType;
+}
+
+static const char * s_WeaponAliasInfo[] =
+{
+ "none", // WEAPON_NONE = 0,
+
+ //Melee
+ "shotgun", //WEAPON_AMERKNIFE,
+
+ NULL, // end of list marker
+};
+
+
+// ----------------------------------------------------------------------------- //
+// CWeaponHL2MPBase tables.
+// ----------------------------------------------------------------------------- //
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponHL2MPBase, DT_WeaponHL2MPBase )
+
+BEGIN_NETWORK_TABLE( CWeaponHL2MPBase, DT_WeaponHL2MPBase )
+
+#ifdef CLIENT_DLL
+
+#else
+ // world weapon models have no aminations
+ // SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ),
+// SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
+// SendPropExclude( "DT_LocalActiveWeaponData", "m_flTimeWeaponIdle" ),
+#endif
+
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponHL2MPBase )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_hl2mp_base, CWeaponHL2MPBase );
+
+
+#ifdef GAME_DLL
+
+ BEGIN_DATADESC( CWeaponHL2MPBase )
+
+ END_DATADESC()
+
+#endif
+
+// ----------------------------------------------------------------------------- //
+// CWeaponHL2MPBase implementation.
+// ----------------------------------------------------------------------------- //
+CWeaponHL2MPBase::CWeaponHL2MPBase()
+{
+ SetPredictionEligible( true );
+ AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches.
+
+ m_flNextResetCheckTime = 0.0f;
+}
+
+
+bool CWeaponHL2MPBase::IsPredicted() const
+{
+ return true;
+}
+
+void CWeaponHL2MPBase::WeaponSound( WeaponSound_t sound_type, float soundtime /* = 0.0f */ )
+{
+#ifdef CLIENT_DLL
+
+ // If we have some sounds from the weapon classname.txt file, play a random one of them
+ const char *shootsound = GetWpnData().aShootSounds[ sound_type ];
+ if ( !shootsound || !shootsound[0] )
+ return;
+
+ CBroadcastRecipientFilter filter; // this is client side only
+ if ( !te->CanPredict() )
+ return;
+
+ CBaseEntity::EmitSound( filter, GetPlayerOwner()->entindex(), shootsound, &GetPlayerOwner()->GetAbsOrigin() );
+#else
+ BaseClass::WeaponSound( sound_type, soundtime );
+#endif
+}
+
+
+CBasePlayer* CWeaponHL2MPBase::GetPlayerOwner() const
+{
+ return dynamic_cast< CBasePlayer* >( GetOwner() );
+}
+
+CHL2MP_Player* CWeaponHL2MPBase::GetHL2MPPlayerOwner() const
+{
+ return dynamic_cast< CHL2MP_Player* >( GetOwner() );
+}
+
+#ifdef CLIENT_DLL
+
+void CWeaponHL2MPBase::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if ( GetPredictable() && !ShouldPredict() )
+ ShutdownPredictable();
+}
+
+
+bool CWeaponHL2MPBase::ShouldPredict()
+{
+ if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+}
+
+
+#else
+
+void CWeaponHL2MPBase::Spawn()
+{
+ BaseClass::Spawn();
+
+ // Set this here to allow players to shoot dropped weapons
+ SetCollisionGroup( COLLISION_GROUP_WEAPON );
+}
+
+void CWeaponHL2MPBase::Materialize( void )
+{
+ if ( IsEffectActive( EF_NODRAW ) )
+ {
+ // changing from invisible state to visible.
+ EmitSound( "AlyxEmp.Charge" );
+
+ RemoveEffects( EF_NODRAW );
+ DoMuzzleFlash();
+ }
+
+ if ( HasSpawnFlags( SF_NORESPAWN ) == false )
+ {
+ VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false );
+ SetMoveType( MOVETYPE_VPHYSICS );
+
+ HL2MPRules()->AddLevelDesignerPlacedObject( this );
+ }
+
+ if ( HasSpawnFlags( SF_NORESPAWN ) == false )
+ {
+ if ( GetOriginalSpawnOrigin() == vec3_origin )
+ {
+ m_vOriginalSpawnOrigin = GetAbsOrigin();
+ m_vOriginalSpawnAngles = GetAbsAngles();
+ }
+ }
+
+ SetPickupTouch();
+
+ SetThink (NULL);
+}
+
+int CWeaponHL2MPBase::ObjectCaps()
+{
+ return BaseClass::ObjectCaps() & ~FCAP_IMPULSE_USE;
+}
+
+#endif
+
+void CWeaponHL2MPBase::FallInit( void )
+{
+#ifndef CLIENT_DLL
+ SetModel( GetWorldModel() );
+ VPhysicsDestroyObject();
+
+ if ( HasSpawnFlags( SF_NORESPAWN ) == false )
+ {
+ SetMoveType( MOVETYPE_NONE );
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_TRIGGER );
+
+ UTIL_DropToFloor( this, MASK_SOLID );
+ }
+ else
+ {
+ if ( !VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ) )
+ {
+ SetMoveType( MOVETYPE_NONE );
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_TRIGGER );
+ }
+ else
+ {
+ #if !defined( CLIENT_DLL )
+ // Constrained start?
+ if ( HasSpawnFlags( SF_WEAPON_START_CONSTRAINED ) )
+ {
+ //Constrain the weapon in place
+ IPhysicsObject *pReferenceObject, *pAttachedObject;
+
+ pReferenceObject = g_PhysWorldObject;
+ pAttachedObject = VPhysicsGetObject();
+
+ if ( pReferenceObject && pAttachedObject )
+ {
+ constraint_fixedparams_t fixed;
+ fixed.Defaults();
+ fixed.InitWithCurrentObjectState( pReferenceObject, pAttachedObject );
+
+ fixed.constraint.forceLimit = lbs2kg( 10000 );
+ fixed.constraint.torqueLimit = lbs2kg( 10000 );
+
+ IPhysicsConstraint *pConstraint = GetConstraint();
+
+ pConstraint = physenv->CreateFixedConstraint( pReferenceObject, pAttachedObject, NULL, fixed );
+
+ pConstraint->SetGameData( (void *) this );
+ }
+ }
+ #endif //CLIENT_DLL
+ }
+ }
+
+ SetPickupTouch();
+
+ SetThink( &CBaseCombatWeapon::FallThink );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+#endif
+}
+
+const CHL2MPSWeaponInfo &CWeaponHL2MPBase::GetHL2MPWpnData() const
+{
+ const FileWeaponInfo_t *pWeaponInfo = &GetWpnData();
+ const CHL2MPSWeaponInfo *pHL2MPInfo;
+
+ #ifdef _DEBUG
+ pHL2MPInfo = dynamic_cast< const CHL2MPSWeaponInfo* >( pWeaponInfo );
+ Assert( pHL2MPInfo );
+ #else
+ pHL2MPInfo = static_cast< const CHL2MPSWeaponInfo* >( pWeaponInfo );
+ #endif
+
+ return *pHL2MPInfo;
+}
+void CWeaponHL2MPBase::FireBullets( const FireBulletsInfo_t &info )
+{
+ FireBulletsInfo_t modinfo = info;
+
+ modinfo.m_iPlayerDamage = GetHL2MPWpnData().m_iPlayerDamage;
+
+ BaseClass::FireBullets( modinfo );
+}
+
+
+#if defined( CLIENT_DLL )
+
+#include "c_te_effect_dispatch.h"
+
+#define NUM_MUZZLE_FLASH_TYPES 4
+
+bool CWeaponHL2MPBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
+}
+
+
+void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip )
+{
+ QAngle final = in + punch;
+
+ //Clip each component
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( final[i] > clip[i] )
+ {
+ final[i] = clip[i];
+ }
+ else if ( final[i] < -clip[i] )
+ {
+ final[i] = -clip[i];
+ }
+
+ //Return the result
+ in[i] = final[i] - punch[i];
+ }
+}
+
+#endif
+
diff --git a/mp/src/game/shared/hl2mp/weapon_hl2mpbase.h b/mp/src/game/shared/hl2mp/weapon_hl2mpbase.h
index ea909a07..0f21044b 100644
--- a/mp/src/game/shared/hl2mp/weapon_hl2mpbase.h
+++ b/mp/src/game/shared/hl2mp/weapon_hl2mpbase.h
@@ -1,93 +1,93 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef WEAPON_HL2MPBASE_H
-#define WEAPON_HL2MPBASE_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-#include "hl2mp_player_shared.h"
-#include "basecombatweapon_shared.h"
-#include "hl2mp_weapon_parse.h"
-
-#if defined( CLIENT_DLL )
- #define CWeaponHL2MPBase C_WeaponHL2MPBase
- void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip );
-#endif
-
-class CHL2MP_Player;
-
-// These are the names of the ammo types that go in the CAmmoDefs and that the
-// weapon script files reference.
-
-// Given an ammo type (like from a weapon's GetPrimaryAmmoType()), this compares it
-// against the ammo name you specify.
-// MIKETODO: this should use indexing instead of searching and strcmp()'ing all the time.
-bool IsAmmoType( int iAmmoType, const char *pAmmoName );
-
-class CWeaponHL2MPBase : public CBaseCombatWeapon
-{
-public:
- DECLARE_CLASS( CWeaponHL2MPBase, CBaseCombatWeapon );
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- CWeaponHL2MPBase();
-
- #ifdef GAME_DLL
- DECLARE_DATADESC();
-
- void SendReloadSoundEvent( void );
-
- void Materialize( void );
- virtual int ObjectCaps( void );
- #endif
-
- // All predicted weapons need to implement and return true
- virtual bool IsPredicted() const;
-
- CBasePlayer* GetPlayerOwner() const;
- CHL2MP_Player* GetHL2MPPlayerOwner() const;
-
- void WeaponSound( WeaponSound_t sound_type, float soundtime = 0.0f );
-
- CHL2MPSWeaponInfo const &GetHL2MPWpnData() const;
-
-
- virtual void FireBullets( const FireBulletsInfo_t &info );
- virtual void FallInit( void );
-
-public:
- #if defined( CLIENT_DLL )
-
- virtual bool ShouldPredict();
- virtual void OnDataChanged( DataUpdateType_t type );
-
- virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
-
- #else
-
- virtual void Spawn();
-
- #endif
-
- float m_flPrevAnimTime;
- float m_flNextResetCheckTime;
-
- Vector GetOriginalSpawnOrigin( void ) { return m_vOriginalSpawnOrigin; }
- QAngle GetOriginalSpawnAngles( void ) { return m_vOriginalSpawnAngles; }
-
-private:
-
- CWeaponHL2MPBase( const CWeaponHL2MPBase & );
-
- Vector m_vOriginalSpawnOrigin;
- QAngle m_vOriginalSpawnAngles;
-};
-
-
-#endif // WEAPON_HL2MPBASE_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_HL2MPBASE_H
+#define WEAPON_HL2MPBASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "hl2mp_player_shared.h"
+#include "basecombatweapon_shared.h"
+#include "hl2mp_weapon_parse.h"
+
+#if defined( CLIENT_DLL )
+ #define CWeaponHL2MPBase C_WeaponHL2MPBase
+ void UTIL_ClipPunchAngleOffset( QAngle &in, const QAngle &punch, const QAngle &clip );
+#endif
+
+class CHL2MP_Player;
+
+// These are the names of the ammo types that go in the CAmmoDefs and that the
+// weapon script files reference.
+
+// Given an ammo type (like from a weapon's GetPrimaryAmmoType()), this compares it
+// against the ammo name you specify.
+// MIKETODO: this should use indexing instead of searching and strcmp()'ing all the time.
+bool IsAmmoType( int iAmmoType, const char *pAmmoName );
+
+class CWeaponHL2MPBase : public CBaseCombatWeapon
+{
+public:
+ DECLARE_CLASS( CWeaponHL2MPBase, CBaseCombatWeapon );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponHL2MPBase();
+
+ #ifdef GAME_DLL
+ DECLARE_DATADESC();
+
+ void SendReloadSoundEvent( void );
+
+ void Materialize( void );
+ virtual int ObjectCaps( void );
+ #endif
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted() const;
+
+ CBasePlayer* GetPlayerOwner() const;
+ CHL2MP_Player* GetHL2MPPlayerOwner() const;
+
+ void WeaponSound( WeaponSound_t sound_type, float soundtime = 0.0f );
+
+ CHL2MPSWeaponInfo const &GetHL2MPWpnData() const;
+
+
+ virtual void FireBullets( const FireBulletsInfo_t &info );
+ virtual void FallInit( void );
+
+public:
+ #if defined( CLIENT_DLL )
+
+ virtual bool ShouldPredict();
+ virtual void OnDataChanged( DataUpdateType_t type );
+
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+
+ #else
+
+ virtual void Spawn();
+
+ #endif
+
+ float m_flPrevAnimTime;
+ float m_flNextResetCheckTime;
+
+ Vector GetOriginalSpawnOrigin( void ) { return m_vOriginalSpawnOrigin; }
+ QAngle GetOriginalSpawnAngles( void ) { return m_vOriginalSpawnAngles; }
+
+private:
+
+ CWeaponHL2MPBase( const CWeaponHL2MPBase & );
+
+ Vector m_vOriginalSpawnOrigin;
+ QAngle m_vOriginalSpawnAngles;
+};
+
+
+#endif // WEAPON_HL2MPBASE_H
diff --git a/mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp b/mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp
index b6cedc1f..b5a0040e 100644
--- a/mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.cpp
@@ -1,242 +1,242 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-
-#if defined( CLIENT_DLL )
- #include "c_hl2mp_player.h"
-#else
- #include "hl2mp_player.h"
-#endif
-
-#include "weapon_hl2mpbase_machinegun.h"
-#include "in_buttons.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-IMPLEMENT_NETWORKCLASS_ALIASED( HL2MPMachineGun, DT_HL2MPMachineGun )
-
-BEGIN_NETWORK_TABLE( CHL2MPMachineGun, DT_HL2MPMachineGun )
-END_NETWORK_TABLE()
-
-BEGIN_PREDICTION_DATA( CHL2MPMachineGun )
-END_PREDICTION_DATA()
-
-//=========================================================
-// >> CHLSelectFireMachineGun
-//=========================================================
-BEGIN_DATADESC( CHL2MPMachineGun )
-
- DEFINE_FIELD( m_nShotsFired, FIELD_INTEGER ),
- DEFINE_FIELD( m_flNextSoundTime, FIELD_TIME ),
-
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CHL2MPMachineGun::CHL2MPMachineGun( void )
-{
-}
-
-const Vector &CHL2MPMachineGun::GetBulletSpread( void )
-{
- static Vector cone = VECTOR_CONE_3DEGREES;
- return cone;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CHL2MPMachineGun::PrimaryAttack( void )
-{
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
- if (!pPlayer)
- return;
-
- // Abort here to handle burst and auto fire modes
- if ( (UsesClipsForAmmo1() && m_iClip1 == 0) || ( !UsesClipsForAmmo1() && !pPlayer->GetAmmoCount(m_iPrimaryAmmoType) ) )
- return;
-
- m_nShotsFired++;
-
- pPlayer->DoMuzzleFlash();
-
- // To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems,
- // especially if the weapon we're firing has a really fast rate of fire.
- int iBulletsToFire = 0;
- float fireRate = GetFireRate();
-
- while ( m_flNextPrimaryAttack <= gpGlobals->curtime )
- {
- // MUST call sound before removing a round from the clip of a CHLMachineGun
- WeaponSound(SINGLE, m_flNextPrimaryAttack);
- m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate;
- iBulletsToFire++;
- }
-
- // Make sure we don't fire more than the amount in the clip, if this weapon uses clips
- if ( UsesClipsForAmmo1() )
- {
- if ( iBulletsToFire > m_iClip1 )
- iBulletsToFire = m_iClip1;
- m_iClip1 -= iBulletsToFire;
- }
-
- CHL2MP_Player *pHL2MPPlayer = ToHL2MPPlayer( pPlayer );
-
- // Fire the bullets
- FireBulletsInfo_t info;
- info.m_iShots = iBulletsToFire;
- info.m_vecSrc = pHL2MPPlayer->Weapon_ShootPosition( );
- info.m_vecDirShooting = pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
- info.m_vecSpread = pHL2MPPlayer->GetAttackSpread( this );
- info.m_flDistance = MAX_TRACE_LENGTH;
- info.m_iAmmoType = m_iPrimaryAmmoType;
- info.m_iTracerFreq = 2;
- FireBullets( info );
-
- //Factor in the view kick
- AddViewKick();
-
- if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- {
- // HEV suit - indicate out of ammo condition
- pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
- }
-
- SendWeaponAnim( GetPrimaryAttackActivity() );
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &info -
-//-----------------------------------------------------------------------------
-void CHL2MPMachineGun::FireBullets( const FireBulletsInfo_t &info )
-{
- if(CBasePlayer *pPlayer = ToBasePlayer ( GetOwner() ) )
- {
- pPlayer->FireBullets(info);
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CHL2MPMachineGun::DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime )
-{
- #define KICK_MIN_X 0.2f //Degrees
- #define KICK_MIN_Y 0.2f //Degrees
- #define KICK_MIN_Z 0.1f //Degrees
-
- QAngle vecScratch;
- int iSeed = CBaseEntity::GetPredictionRandomSeed() & 255;
-
- //Find how far into our accuracy degradation we are
- float duration = ( fireDurationTime > slideLimitTime ) ? slideLimitTime : fireDurationTime;
- float kickPerc = duration / slideLimitTime;
-
- // do this to get a hard discontinuity, clear out anything under 10 degrees punch
- pPlayer->ViewPunchReset( 10 );
-
- //Apply this to the view angles as well
- vecScratch.x = -( KICK_MIN_X + ( maxVerticleKickAngle * kickPerc ) );
- vecScratch.y = -( KICK_MIN_Y + ( maxVerticleKickAngle * kickPerc ) ) / 3;
- vecScratch.z = KICK_MIN_Z + ( maxVerticleKickAngle * kickPerc ) / 8;
-
- RandomSeed( iSeed );
-
- //Wibble left and right
- if ( RandomInt( -1, 1 ) >= 0 )
- vecScratch.y *= -1;
-
- iSeed++;
-
- //Wobble up and down
- if ( RandomInt( -1, 1 ) >= 0 )
- vecScratch.z *= -1;
-
- //Clip this to our desired min/max
- UTIL_ClipPunchAngleOffset( vecScratch, pPlayer->m_Local.m_vecPunchAngle, QAngle( 24.0f, 3.0f, 1.0f ) );
-
- //Add it to the view punch
- // NOTE: 0.5 is just tuned to match the old effect before the punch became simulated
- pPlayer->ViewPunch( vecScratch * 0.5 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Reset our shots fired
-//-----------------------------------------------------------------------------
-bool CHL2MPMachineGun::Deploy( void )
-{
- m_nShotsFired = 0;
-
- return BaseClass::Deploy();
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Make enough sound events to fill the estimated think interval
-// returns: number of shots needed
-//-----------------------------------------------------------------------------
-int CHL2MPMachineGun::WeaponSoundRealtime( WeaponSound_t shoot_type )
-{
- int numBullets = 0;
-
- // ran out of time, clamp to current
- if (m_flNextSoundTime < gpGlobals->curtime)
- {
- m_flNextSoundTime = gpGlobals->curtime;
- }
-
- // make enough sound events to fill up the next estimated think interval
- float dt = clamp( m_flAnimTime - m_flPrevAnimTime, 0, 0.2 );
- if (m_flNextSoundTime < gpGlobals->curtime + dt)
- {
- WeaponSound( SINGLE_NPC, m_flNextSoundTime );
- m_flNextSoundTime += GetFireRate();
- numBullets++;
- }
- if (m_flNextSoundTime < gpGlobals->curtime + dt)
- {
- WeaponSound( SINGLE_NPC, m_flNextSoundTime );
- m_flNextSoundTime += GetFireRate();
- numBullets++;
- }
-
- return numBullets;
-}
-
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CHL2MPMachineGun::ItemPostFrame( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- // Debounce the recoiling counter
- if ( ( pOwner->m_nButtons & IN_ATTACK ) == false )
- {
- m_nShotsFired = 0;
- }
-
- BaseClass::ItemPostFrame();
-}
-
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+
+#if defined( CLIENT_DLL )
+ #include "c_hl2mp_player.h"
+#else
+ #include "hl2mp_player.h"
+#endif
+
+#include "weapon_hl2mpbase_machinegun.h"
+#include "in_buttons.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+IMPLEMENT_NETWORKCLASS_ALIASED( HL2MPMachineGun, DT_HL2MPMachineGun )
+
+BEGIN_NETWORK_TABLE( CHL2MPMachineGun, DT_HL2MPMachineGun )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CHL2MPMachineGun )
+END_PREDICTION_DATA()
+
+//=========================================================
+// >> CHLSelectFireMachineGun
+//=========================================================
+BEGIN_DATADESC( CHL2MPMachineGun )
+
+ DEFINE_FIELD( m_nShotsFired, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flNextSoundTime, FIELD_TIME ),
+
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHL2MPMachineGun::CHL2MPMachineGun( void )
+{
+}
+
+const Vector &CHL2MPMachineGun::GetBulletSpread( void )
+{
+ static Vector cone = VECTOR_CONE_3DEGREES;
+ return cone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+//
+//-----------------------------------------------------------------------------
+void CHL2MPMachineGun::PrimaryAttack( void )
+{
+ // Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if (!pPlayer)
+ return;
+
+ // Abort here to handle burst and auto fire modes
+ if ( (UsesClipsForAmmo1() && m_iClip1 == 0) || ( !UsesClipsForAmmo1() && !pPlayer->GetAmmoCount(m_iPrimaryAmmoType) ) )
+ return;
+
+ m_nShotsFired++;
+
+ pPlayer->DoMuzzleFlash();
+
+ // To make the firing framerate independent, we may have to fire more than one bullet here on low-framerate systems,
+ // especially if the weapon we're firing has a really fast rate of fire.
+ int iBulletsToFire = 0;
+ float fireRate = GetFireRate();
+
+ while ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ // MUST call sound before removing a round from the clip of a CHLMachineGun
+ WeaponSound(SINGLE, m_flNextPrimaryAttack);
+ m_flNextPrimaryAttack = m_flNextPrimaryAttack + fireRate;
+ iBulletsToFire++;
+ }
+
+ // Make sure we don't fire more than the amount in the clip, if this weapon uses clips
+ if ( UsesClipsForAmmo1() )
+ {
+ if ( iBulletsToFire > m_iClip1 )
+ iBulletsToFire = m_iClip1;
+ m_iClip1 -= iBulletsToFire;
+ }
+
+ CHL2MP_Player *pHL2MPPlayer = ToHL2MPPlayer( pPlayer );
+
+ // Fire the bullets
+ FireBulletsInfo_t info;
+ info.m_iShots = iBulletsToFire;
+ info.m_vecSrc = pHL2MPPlayer->Weapon_ShootPosition( );
+ info.m_vecDirShooting = pPlayer->GetAutoaimVector( AUTOAIM_5DEGREES );
+ info.m_vecSpread = pHL2MPPlayer->GetAttackSpread( this );
+ info.m_flDistance = MAX_TRACE_LENGTH;
+ info.m_iAmmoType = m_iPrimaryAmmoType;
+ info.m_iTracerFreq = 2;
+ FireBullets( info );
+
+ //Factor in the view kick
+ AddViewKick();
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
+ }
+
+ SendWeaponAnim( GetPrimaryAttackActivity() );
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &info -
+//-----------------------------------------------------------------------------
+void CHL2MPMachineGun::FireBullets( const FireBulletsInfo_t &info )
+{
+ if(CBasePlayer *pPlayer = ToBasePlayer ( GetOwner() ) )
+ {
+ pPlayer->FireBullets(info);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHL2MPMachineGun::DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime )
+{
+ #define KICK_MIN_X 0.2f //Degrees
+ #define KICK_MIN_Y 0.2f //Degrees
+ #define KICK_MIN_Z 0.1f //Degrees
+
+ QAngle vecScratch;
+ int iSeed = CBaseEntity::GetPredictionRandomSeed() & 255;
+
+ //Find how far into our accuracy degradation we are
+ float duration = ( fireDurationTime > slideLimitTime ) ? slideLimitTime : fireDurationTime;
+ float kickPerc = duration / slideLimitTime;
+
+ // do this to get a hard discontinuity, clear out anything under 10 degrees punch
+ pPlayer->ViewPunchReset( 10 );
+
+ //Apply this to the view angles as well
+ vecScratch.x = -( KICK_MIN_X + ( maxVerticleKickAngle * kickPerc ) );
+ vecScratch.y = -( KICK_MIN_Y + ( maxVerticleKickAngle * kickPerc ) ) / 3;
+ vecScratch.z = KICK_MIN_Z + ( maxVerticleKickAngle * kickPerc ) / 8;
+
+ RandomSeed( iSeed );
+
+ //Wibble left and right
+ if ( RandomInt( -1, 1 ) >= 0 )
+ vecScratch.y *= -1;
+
+ iSeed++;
+
+ //Wobble up and down
+ if ( RandomInt( -1, 1 ) >= 0 )
+ vecScratch.z *= -1;
+
+ //Clip this to our desired min/max
+ UTIL_ClipPunchAngleOffset( vecScratch, pPlayer->m_Local.m_vecPunchAngle, QAngle( 24.0f, 3.0f, 1.0f ) );
+
+ //Add it to the view punch
+ // NOTE: 0.5 is just tuned to match the old effect before the punch became simulated
+ pPlayer->ViewPunch( vecScratch * 0.5 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reset our shots fired
+//-----------------------------------------------------------------------------
+bool CHL2MPMachineGun::Deploy( void )
+{
+ m_nShotsFired = 0;
+
+ return BaseClass::Deploy();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Make enough sound events to fill the estimated think interval
+// returns: number of shots needed
+//-----------------------------------------------------------------------------
+int CHL2MPMachineGun::WeaponSoundRealtime( WeaponSound_t shoot_type )
+{
+ int numBullets = 0;
+
+ // ran out of time, clamp to current
+ if (m_flNextSoundTime < gpGlobals->curtime)
+ {
+ m_flNextSoundTime = gpGlobals->curtime;
+ }
+
+ // make enough sound events to fill up the next estimated think interval
+ float dt = clamp( m_flAnimTime - m_flPrevAnimTime, 0, 0.2 );
+ if (m_flNextSoundTime < gpGlobals->curtime + dt)
+ {
+ WeaponSound( SINGLE_NPC, m_flNextSoundTime );
+ m_flNextSoundTime += GetFireRate();
+ numBullets++;
+ }
+ if (m_flNextSoundTime < gpGlobals->curtime + dt)
+ {
+ WeaponSound( SINGLE_NPC, m_flNextSoundTime );
+ m_flNextSoundTime += GetFireRate();
+ numBullets++;
+ }
+
+ return numBullets;
+}
+
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHL2MPMachineGun::ItemPostFrame( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ // Debounce the recoiling counter
+ if ( ( pOwner->m_nButtons & IN_ATTACK ) == false )
+ {
+ m_nShotsFired = 0;
+ }
+
+ BaseClass::ItemPostFrame();
+}
+
+
diff --git a/mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h b/mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h
index 0be51bbe..c7f9a56b 100644
--- a/mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h
+++ b/mp/src/game/shared/hl2mp/weapon_hl2mpbase_machinegun.h
@@ -1,58 +1,58 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "weapon_hl2mpbase.h"
-
-#ifndef BASEHLCOMBATWEAPON_H
-#define BASEHLCOMBATWEAPON_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-#if defined( CLIENT_DLL )
- #define CHL2MPMachineGun C_HL2MPMachineGun
-#endif
-
-//=========================================================
-// Machine gun base class
-//=========================================================
-class CHL2MPMachineGun : public CWeaponHL2MPBase
-{
-public:
- DECLARE_CLASS( CHL2MPMachineGun, CWeaponHL2MPBase );
- DECLARE_DATADESC();
-
- CHL2MPMachineGun();
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- void PrimaryAttack( void );
-
- // Default calls through to m_hOwner, but plasma weapons can override and shoot projectiles here.
- virtual void ItemPostFrame( void );
- virtual void FireBullets( const FireBulletsInfo_t &info );
- virtual bool Deploy( void );
-
- virtual const Vector &GetBulletSpread( void );
-
- int WeaponSoundRealtime( WeaponSound_t shoot_type );
-
- // utility function
- static void DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime );
-
-private:
-
- CHL2MPMachineGun( const CHL2MPMachineGun & );
-
-protected:
-
- int m_nShotsFired; // Number of consecutive shots fired
-
- float m_flNextSoundTime; // real-time clock of when to make next sound
-};
-
-#endif // BASEHLCOMBATWEAPON_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "weapon_hl2mpbase.h"
+
+#ifndef BASEHLCOMBATWEAPON_H
+#define BASEHLCOMBATWEAPON_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#if defined( CLIENT_DLL )
+ #define CHL2MPMachineGun C_HL2MPMachineGun
+#endif
+
+//=========================================================
+// Machine gun base class
+//=========================================================
+class CHL2MPMachineGun : public CWeaponHL2MPBase
+{
+public:
+ DECLARE_CLASS( CHL2MPMachineGun, CWeaponHL2MPBase );
+ DECLARE_DATADESC();
+
+ CHL2MPMachineGun();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ void PrimaryAttack( void );
+
+ // Default calls through to m_hOwner, but plasma weapons can override and shoot projectiles here.
+ virtual void ItemPostFrame( void );
+ virtual void FireBullets( const FireBulletsInfo_t &info );
+ virtual bool Deploy( void );
+
+ virtual const Vector &GetBulletSpread( void );
+
+ int WeaponSoundRealtime( WeaponSound_t shoot_type );
+
+ // utility function
+ static void DoMachineGunKick( CBasePlayer *pPlayer, float dampEasy, float maxVerticleKickAngle, float fireDurationTime, float slideLimitTime );
+
+private:
+
+ CHL2MPMachineGun( const CHL2MPMachineGun & );
+
+protected:
+
+ int m_nShotsFired; // Number of consecutive shots fired
+
+ float m_flNextSoundTime; // real-time clock of when to make next sound
+};
+
+#endif // BASEHLCOMBATWEAPON_H
diff --git a/mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp b/mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp
index e5306a63..21373ca8 100644
--- a/mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.cpp
@@ -1,363 +1,363 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "weapon_hl2mpbasebasebludgeon.h"
-#include "gamerules.h"
-#include "ammodef.h"
-#include "mathlib/mathlib.h"
-#include "in_buttons.h"
-#include "animation.h"
-
-#if defined( CLIENT_DLL )
- #include "c_hl2mp_player.h"
-#else
- #include "hl2mp_player.h"
- #include "ndebugoverlay.h"
- #include "te_effect_dispatch.h"
- #include "ilagcompensationmanager.h"
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-IMPLEMENT_NETWORKCLASS_ALIASED( BaseHL2MPBludgeonWeapon, DT_BaseHL2MPBludgeonWeapon )
-
-BEGIN_NETWORK_TABLE( CBaseHL2MPBludgeonWeapon, DT_BaseHL2MPBludgeonWeapon )
-END_NETWORK_TABLE()
-
-BEGIN_PREDICTION_DATA( CBaseHL2MPBludgeonWeapon )
-END_PREDICTION_DATA()
-
-#define BLUDGEON_HULL_DIM 16
-
-static const Vector g_bludgeonMins(-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM);
-static const Vector g_bludgeonMaxs(BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM);
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CBaseHL2MPBludgeonWeapon::CBaseHL2MPBludgeonWeapon()
-{
- m_bFiresUnderwater = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Spawn the weapon
-//-----------------------------------------------------------------------------
-void CBaseHL2MPBludgeonWeapon::Spawn( void )
-{
- m_fMinRange1 = 0;
- m_fMinRange2 = 0;
- m_fMaxRange1 = 64;
- m_fMaxRange2 = 64;
- //Call base class first
- BaseClass::Spawn();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Precache the weapon
-//-----------------------------------------------------------------------------
-void CBaseHL2MPBludgeonWeapon::Precache( void )
-{
- //Call base class first
- BaseClass::Precache();
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Update weapon
-//------------------------------------------------------------------------------
-void CBaseHL2MPBludgeonWeapon::ItemPostFrame( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
- {
- PrimaryAttack();
- }
- else if ( (pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime) )
- {
- SecondaryAttack();
- }
- else
- {
- WeaponIdle();
- return;
- }
-}
-
-//------------------------------------------------------------------------------
-// Purpose :
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CBaseHL2MPBludgeonWeapon::PrimaryAttack()
-{
-
-#ifndef CLIENT_DLL
- CHL2MP_Player *pPlayer = ToHL2MPPlayer( GetPlayerOwner() );
- // Move other players back to history positions based on local player's lag
- lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
-#endif
- Swing( false );
-#ifndef CLIENT_DLL
- // Move other players back to history positions based on local player's lag
- lagcompensation->FinishLagCompensation( pPlayer );
-#endif
-
-}
-
-//------------------------------------------------------------------------------
-// Purpose :
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CBaseHL2MPBludgeonWeapon::SecondaryAttack()
-{
- Swing( true );
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose: Implement impact function
-//------------------------------------------------------------------------------
-void CBaseHL2MPBludgeonWeapon::Hit( trace_t &traceHit, Activity nHitActivity )
-{
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- //Do view kick
-// AddViewKick();
-
- CBaseEntity *pHitEntity = traceHit.m_pEnt;
-
- //Apply damage to a hit target
- if ( pHitEntity != NULL )
- {
- Vector hitDirection;
- pPlayer->EyeVectors( &hitDirection, NULL, NULL );
- VectorNormalize( hitDirection );
-
-#ifndef CLIENT_DLL
- CTakeDamageInfo info( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
-
- if( pPlayer && pHitEntity->IsNPC() )
- {
- // If bonking an NPC, adjust damage.
- info.AdjustPlayerDamageInflictedForSkillLevel();
- }
-
- CalculateMeleeDamageForce( &info, hitDirection, traceHit.endpos );
-
- pHitEntity->DispatchTraceAttack( info, hitDirection, &traceHit );
- ApplyMultiDamage();
-
- // Now hit all triggers along the ray that...
- TraceAttackToTriggers( info, traceHit.startpos, traceHit.endpos, hitDirection );
-#endif
- WeaponSound( MELEE_HIT );
- }
-
- // Apply an impact effect
- ImpactEffect( traceHit );
-}
-
-Activity CBaseHL2MPBludgeonWeapon::ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner )
-{
- int i, j, k;
- float distance;
- const float *minmaxs[2] = {mins.Base(), maxs.Base()};
- trace_t tmpTrace;
- Vector vecHullEnd = hitTrace.endpos;
- Vector vecEnd;
-
- distance = 1e6f;
- Vector vecSrc = hitTrace.startpos;
-
- vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
- UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
- if ( tmpTrace.fraction == 1.0 )
- {
- for ( i = 0; i < 2; i++ )
- {
- for ( j = 0; j < 2; j++ )
- {
- for ( k = 0; k < 2; k++ )
- {
- vecEnd.x = vecHullEnd.x + minmaxs[i][0];
- vecEnd.y = vecHullEnd.y + minmaxs[j][1];
- vecEnd.z = vecHullEnd.z + minmaxs[k][2];
-
- UTIL_TraceLine( vecSrc, vecEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
- if ( tmpTrace.fraction < 1.0 )
- {
- float thisDistance = (tmpTrace.endpos - vecSrc).Length();
- if ( thisDistance < distance )
- {
- hitTrace = tmpTrace;
- distance = thisDistance;
- }
- }
- }
- }
- }
- }
- else
- {
- hitTrace = tmpTrace;
- }
-
-
- return ACT_VM_HITCENTER;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &traceHit -
-//-----------------------------------------------------------------------------
-bool CBaseHL2MPBludgeonWeapon::ImpactWater( const Vector &start, const Vector &end )
-{
- //FIXME: This doesn't handle the case of trying to splash while being underwater, but that's not going to look good
- // right now anyway...
-
- // We must start outside the water
- if ( UTIL_PointContents( start ) & (CONTENTS_WATER|CONTENTS_SLIME))
- return false;
-
- // We must end inside of water
- if ( !(UTIL_PointContents( end ) & (CONTENTS_WATER|CONTENTS_SLIME)))
- return false;
-
- trace_t waterTrace;
-
- UTIL_TraceLine( start, end, (CONTENTS_WATER|CONTENTS_SLIME), GetOwner(), COLLISION_GROUP_NONE, &waterTrace );
-
- if ( waterTrace.fraction < 1.0f )
- {
-#ifndef CLIENT_DLL
- CEffectData data;
-
- data.m_fFlags = 0;
- data.m_vOrigin = waterTrace.endpos;
- data.m_vNormal = waterTrace.plane.normal;
- data.m_flScale = 8.0f;
-
- // See if we hit slime
- if ( waterTrace.contents & CONTENTS_SLIME )
- {
- data.m_fFlags |= FX_WATER_IN_SLIME;
- }
-
- DispatchEffect( "watersplash", data );
-#endif
- }
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseHL2MPBludgeonWeapon::ImpactEffect( trace_t &traceHit )
-{
- // See if we hit water (we don't do the other impact effects in this case)
- if ( ImpactWater( traceHit.startpos, traceHit.endpos ) )
- return;
-
- //FIXME: need new decals
- UTIL_ImpactTrace( &traceHit, DMG_CLUB );
-}
-
-
-//------------------------------------------------------------------------------
-// Purpose : Starts the swing of the weapon and determines the animation
-// Input : bIsSecondary - is this a secondary attack?
-//------------------------------------------------------------------------------
-void CBaseHL2MPBludgeonWeapon::Swing( int bIsSecondary )
-{
- 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 );
- Activity nHitActivity = ACT_VM_HITCENTER;
-
-#ifndef CLIENT_DLL
- // Like bullets, bludgeon traces have to trace against triggers.
- CTakeDamageInfo triggerInfo( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
- TraceAttackToTriggers( triggerInfo, traceHit.startpos, traceHit.endpos, vec3_origin );
-#endif
-
- 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
- {
- nHitActivity = ChooseIntersectionPointAndActivity( traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner );
- }
- }
- }
-
- WeaponSound( SINGLE );
-
- // -------------------------
- // Miss
- // -------------------------
- if ( traceHit.fraction == 1.0f )
- {
- nHitActivity = bIsSecondary ? ACT_VM_MISSCENTER2 : ACT_VM_MISSCENTER;
-
- // We want to test the first swing again
- Vector testEnd = swingStart + forward * GetRange();
-
- // See if we happened to hit water
- ImpactWater( swingStart, testEnd );
- }
- else
- {
- Hit( traceHit, nHitActivity );
- }
-
- // Send the anim
- SendWeaponAnim( nHitActivity );
-
- pOwner->SetAnimation( PLAYER_ATTACK1 );
-
- //Setup our next attack times
- m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
- m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_hl2mpbasebasebludgeon.h"
+#include "gamerules.h"
+#include "ammodef.h"
+#include "mathlib/mathlib.h"
+#include "in_buttons.h"
+#include "animation.h"
+
+#if defined( CLIENT_DLL )
+ #include "c_hl2mp_player.h"
+#else
+ #include "hl2mp_player.h"
+ #include "ndebugoverlay.h"
+ #include "te_effect_dispatch.h"
+ #include "ilagcompensationmanager.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseHL2MPBludgeonWeapon, DT_BaseHL2MPBludgeonWeapon )
+
+BEGIN_NETWORK_TABLE( CBaseHL2MPBludgeonWeapon, DT_BaseHL2MPBludgeonWeapon )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CBaseHL2MPBludgeonWeapon )
+END_PREDICTION_DATA()
+
+#define BLUDGEON_HULL_DIM 16
+
+static const Vector g_bludgeonMins(-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM,-BLUDGEON_HULL_DIM);
+static const Vector g_bludgeonMaxs(BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM,BLUDGEON_HULL_DIM);
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CBaseHL2MPBludgeonWeapon::CBaseHL2MPBludgeonWeapon()
+{
+ m_bFiresUnderwater = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Spawn the weapon
+//-----------------------------------------------------------------------------
+void CBaseHL2MPBludgeonWeapon::Spawn( void )
+{
+ m_fMinRange1 = 0;
+ m_fMinRange2 = 0;
+ m_fMaxRange1 = 64;
+ m_fMaxRange2 = 64;
+ //Call base class first
+ BaseClass::Spawn();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache the weapon
+//-----------------------------------------------------------------------------
+void CBaseHL2MPBludgeonWeapon::Precache( void )
+{
+ //Call base class first
+ BaseClass::Precache();
+}
+
+//------------------------------------------------------------------------------
+// Purpose : Update weapon
+//------------------------------------------------------------------------------
+void CBaseHL2MPBludgeonWeapon::ItemPostFrame( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ PrimaryAttack();
+ }
+ else if ( (pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime) )
+ {
+ SecondaryAttack();
+ }
+ else
+ {
+ WeaponIdle();
+ return;
+ }
+}
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHL2MPBludgeonWeapon::PrimaryAttack()
+{
+
+#ifndef CLIENT_DLL
+ CHL2MP_Player *pPlayer = ToHL2MPPlayer( GetPlayerOwner() );
+ // Move other players back to history positions based on local player's lag
+ lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
+#endif
+ Swing( false );
+#ifndef CLIENT_DLL
+ // Move other players back to history positions based on local player's lag
+ lagcompensation->FinishLagCompensation( pPlayer );
+#endif
+
+}
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CBaseHL2MPBludgeonWeapon::SecondaryAttack()
+{
+ Swing( true );
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose: Implement impact function
+//------------------------------------------------------------------------------
+void CBaseHL2MPBludgeonWeapon::Hit( trace_t &traceHit, Activity nHitActivity )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ //Do view kick
+// AddViewKick();
+
+ CBaseEntity *pHitEntity = traceHit.m_pEnt;
+
+ //Apply damage to a hit target
+ if ( pHitEntity != NULL )
+ {
+ Vector hitDirection;
+ pPlayer->EyeVectors( &hitDirection, NULL, NULL );
+ VectorNormalize( hitDirection );
+
+#ifndef CLIENT_DLL
+ CTakeDamageInfo info( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
+
+ if( pPlayer && pHitEntity->IsNPC() )
+ {
+ // If bonking an NPC, adjust damage.
+ info.AdjustPlayerDamageInflictedForSkillLevel();
+ }
+
+ CalculateMeleeDamageForce( &info, hitDirection, traceHit.endpos );
+
+ pHitEntity->DispatchTraceAttack( info, hitDirection, &traceHit );
+ ApplyMultiDamage();
+
+ // Now hit all triggers along the ray that...
+ TraceAttackToTriggers( info, traceHit.startpos, traceHit.endpos, hitDirection );
+#endif
+ WeaponSound( MELEE_HIT );
+ }
+
+ // Apply an impact effect
+ ImpactEffect( traceHit );
+}
+
+Activity CBaseHL2MPBludgeonWeapon::ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner )
+{
+ int i, j, k;
+ float distance;
+ const float *minmaxs[2] = {mins.Base(), maxs.Base()};
+ trace_t tmpTrace;
+ Vector vecHullEnd = hitTrace.endpos;
+ Vector vecEnd;
+
+ distance = 1e6f;
+ Vector vecSrc = hitTrace.startpos;
+
+ vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
+ UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
+ if ( tmpTrace.fraction == 1.0 )
+ {
+ for ( i = 0; i < 2; i++ )
+ {
+ for ( j = 0; j < 2; j++ )
+ {
+ for ( k = 0; k < 2; k++ )
+ {
+ vecEnd.x = vecHullEnd.x + minmaxs[i][0];
+ vecEnd.y = vecHullEnd.y + minmaxs[j][1];
+ vecEnd.z = vecHullEnd.z + minmaxs[k][2];
+
+ UTIL_TraceLine( vecSrc, vecEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
+ if ( tmpTrace.fraction < 1.0 )
+ {
+ float thisDistance = (tmpTrace.endpos - vecSrc).Length();
+ if ( thisDistance < distance )
+ {
+ hitTrace = tmpTrace;
+ distance = thisDistance;
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ hitTrace = tmpTrace;
+ }
+
+
+ return ACT_VM_HITCENTER;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &traceHit -
+//-----------------------------------------------------------------------------
+bool CBaseHL2MPBludgeonWeapon::ImpactWater( const Vector &start, const Vector &end )
+{
+ //FIXME: This doesn't handle the case of trying to splash while being underwater, but that's not going to look good
+ // right now anyway...
+
+ // We must start outside the water
+ if ( UTIL_PointContents( start ) & (CONTENTS_WATER|CONTENTS_SLIME))
+ return false;
+
+ // We must end inside of water
+ if ( !(UTIL_PointContents( end ) & (CONTENTS_WATER|CONTENTS_SLIME)))
+ return false;
+
+ trace_t waterTrace;
+
+ UTIL_TraceLine( start, end, (CONTENTS_WATER|CONTENTS_SLIME), GetOwner(), COLLISION_GROUP_NONE, &waterTrace );
+
+ if ( waterTrace.fraction < 1.0f )
+ {
+#ifndef CLIENT_DLL
+ CEffectData data;
+
+ data.m_fFlags = 0;
+ data.m_vOrigin = waterTrace.endpos;
+ data.m_vNormal = waterTrace.plane.normal;
+ data.m_flScale = 8.0f;
+
+ // See if we hit slime
+ if ( waterTrace.contents & CONTENTS_SLIME )
+ {
+ data.m_fFlags |= FX_WATER_IN_SLIME;
+ }
+
+ DispatchEffect( "watersplash", data );
+#endif
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseHL2MPBludgeonWeapon::ImpactEffect( trace_t &traceHit )
+{
+ // See if we hit water (we don't do the other impact effects in this case)
+ if ( ImpactWater( traceHit.startpos, traceHit.endpos ) )
+ return;
+
+ //FIXME: need new decals
+ UTIL_ImpactTrace( &traceHit, DMG_CLUB );
+}
+
+
+//------------------------------------------------------------------------------
+// Purpose : Starts the swing of the weapon and determines the animation
+// Input : bIsSecondary - is this a secondary attack?
+//------------------------------------------------------------------------------
+void CBaseHL2MPBludgeonWeapon::Swing( int bIsSecondary )
+{
+ 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 );
+ Activity nHitActivity = ACT_VM_HITCENTER;
+
+#ifndef CLIENT_DLL
+ // Like bullets, bludgeon traces have to trace against triggers.
+ CTakeDamageInfo triggerInfo( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
+ TraceAttackToTriggers( triggerInfo, traceHit.startpos, traceHit.endpos, vec3_origin );
+#endif
+
+ 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
+ {
+ nHitActivity = ChooseIntersectionPointAndActivity( traceHit, g_bludgeonMins, g_bludgeonMaxs, pOwner );
+ }
+ }
+ }
+
+ WeaponSound( SINGLE );
+
+ // -------------------------
+ // Miss
+ // -------------------------
+ if ( traceHit.fraction == 1.0f )
+ {
+ nHitActivity = bIsSecondary ? ACT_VM_MISSCENTER2 : ACT_VM_MISSCENTER;
+
+ // We want to test the first swing again
+ Vector testEnd = swingStart + forward * GetRange();
+
+ // See if we happened to hit water
+ ImpactWater( swingStart, testEnd );
+ }
+ else
+ {
+ Hit( traceHit, nHitActivity );
+ }
+
+ // Send the anim
+ SendWeaponAnim( nHitActivity );
+
+ pOwner->SetAnimation( PLAYER_ATTACK1 );
+
+ //Setup our next attack times
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
+}
diff --git a/mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h b/mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h
index df5ef041..d268b321 100644
--- a/mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h
+++ b/mp/src/game/shared/hl2mp/weapon_hl2mpbasebasebludgeon.h
@@ -1,66 +1,66 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: The class from which all bludgeon melee
-// weapons are derived.
-//
-// $Workfile: $
-// $Date: $
-// $NoKeywords: $
-//=============================================================================//
-
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-
-#ifndef BASEBLUDGEONWEAPON_H
-#define BASEBLUDGEONWEAPON_H
-
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-#if defined( CLIENT_DLL )
-#define CBaseHL2MPBludgeonWeapon C_BaseHL2MPBludgeonWeapon
-#endif
-
-//=========================================================
-// CBaseHLBludgeonWeapon
-//=========================================================
-class CBaseHL2MPBludgeonWeapon : public CBaseHL2MPCombatWeapon
-{
- DECLARE_CLASS( CBaseHL2MPBludgeonWeapon, CBaseHL2MPCombatWeapon );
-public:
- CBaseHL2MPBludgeonWeapon();
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- virtual void Spawn( void );
- virtual void Precache( void );
-
- //Attack functions
- virtual void PrimaryAttack( void );
- virtual void SecondaryAttack( void );
-
- virtual void ItemPostFrame( void );
-
- //Functions to select animation sequences
- virtual Activity GetPrimaryAttackActivity( void ) { return ACT_VM_HITCENTER; }
- virtual Activity GetSecondaryAttackActivity( void ) { return ACT_VM_HITCENTER2; }
-
- virtual float GetFireRate( void ) { return 0.2f; }
- virtual float GetRange( void ) { return 32.0f; }
- virtual float GetDamageForActivity( Activity hitActivity ) { return 1.0f; }
-
- CBaseHL2MPBludgeonWeapon( const CBaseHL2MPBludgeonWeapon & );
-
-protected:
- virtual void ImpactEffect( trace_t &trace );
-
-private:
- bool ImpactWater( const Vector &start, const Vector &end );
- void Swing( int bIsSecondary );
- void Hit( trace_t &traceHit, Activity nHitActivity );
- Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner );
-};
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The class from which all bludgeon melee
+// weapons are derived.
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+
+#ifndef BASEBLUDGEONWEAPON_H
+#define BASEBLUDGEONWEAPON_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#if defined( CLIENT_DLL )
+#define CBaseHL2MPBludgeonWeapon C_BaseHL2MPBludgeonWeapon
+#endif
+
+//=========================================================
+// CBaseHLBludgeonWeapon
+//=========================================================
+class CBaseHL2MPBludgeonWeapon : public CBaseHL2MPCombatWeapon
+{
+ DECLARE_CLASS( CBaseHL2MPBludgeonWeapon, CBaseHL2MPCombatWeapon );
+public:
+ CBaseHL2MPBludgeonWeapon();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+
+ //Attack functions
+ virtual void PrimaryAttack( void );
+ virtual void SecondaryAttack( void );
+
+ virtual void ItemPostFrame( void );
+
+ //Functions to select animation sequences
+ virtual Activity GetPrimaryAttackActivity( void ) { return ACT_VM_HITCENTER; }
+ virtual Activity GetSecondaryAttackActivity( void ) { return ACT_VM_HITCENTER2; }
+
+ virtual float GetFireRate( void ) { return 0.2f; }
+ virtual float GetRange( void ) { return 32.0f; }
+ virtual float GetDamageForActivity( Activity hitActivity ) { return 1.0f; }
+
+ CBaseHL2MPBludgeonWeapon( const CBaseHL2MPBludgeonWeapon & );
+
+protected:
+ virtual void ImpactEffect( trace_t &trace );
+
+private:
+ bool ImpactWater( const Vector &start, const Vector &end );
+ void Swing( int bIsSecondary );
+ void Hit( trace_t &traceHit, Activity nHitActivity );
+ Activity ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CBasePlayer *pOwner );
+};
+
#endif \ No newline at end of file
diff --git a/mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp b/mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp
index 42a2e0e3..361a53f1 100644
--- a/mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.cpp
@@ -1,416 +1,416 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-
-#include "hl2mp_player_shared.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-LINK_ENTITY_TO_CLASS( basehl2mpcombatweapon, CBaseHL2MPCombatWeapon );
-
-IMPLEMENT_NETWORKCLASS_ALIASED( BaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon )
-
-BEGIN_NETWORK_TABLE( CBaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon )
-#if !defined( CLIENT_DLL )
-// SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ),
-#else
-// RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ),
-#endif
-END_NETWORK_TABLE()
-
-
-#if !defined( CLIENT_DLL )
-
-#include "globalstate.h"
-
-//---------------------------------------------------------
-// Save/Restore
-//---------------------------------------------------------
-BEGIN_DATADESC( CBaseHL2MPCombatWeapon )
-
- DEFINE_FIELD( m_bLowered, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_flRaiseTime, FIELD_TIME ),
- DEFINE_FIELD( m_flHolsterTime, FIELD_TIME ),
-
-END_DATADESC()
-
-#endif
-
-BEGIN_PREDICTION_DATA( CBaseHL2MPCombatWeapon )
-END_PREDICTION_DATA()
-
-extern ConVar sk_auto_reload_time;
-
-CBaseHL2MPCombatWeapon::CBaseHL2MPCombatWeapon( void )
-{
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CBaseHL2MPCombatWeapon::ItemHolsterFrame( void )
-{
- BaseClass::ItemHolsterFrame();
-
- // Must be player held
- if ( GetOwner() && GetOwner()->IsPlayer() == false )
- return;
-
- // We can't be active
- if ( GetOwner()->GetActiveWeapon() == this )
- return;
-
- // If it's been longer than three seconds, reload
- if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() )
- {
- // Just load the clip with no animations
- FinishReload();
- m_flHolsterTime = gpGlobals->curtime;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Drops the weapon into a lowered pose
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseHL2MPCombatWeapon::Lower( void )
-{
- //Don't bother if we don't have the animation
- if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE )
- return false;
-
- m_bLowered = true;
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Brings the weapon up to the ready position
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseHL2MPCombatWeapon::Ready( void )
-{
- //Don't bother if we don't have the animation
- if ( SelectWeightedSequence( ACT_VM_LOWERED_TO_IDLE ) == ACTIVITY_NOT_AVAILABLE )
- return false;
-
- m_bLowered = false;
- m_flRaiseTime = gpGlobals->curtime + 0.5f;
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseHL2MPCombatWeapon::Deploy( void )
-{
- // If we should be lowered, deploy in the lowered position
- // We have to ask the player if the last time it checked, the weapon was lowered
- if ( GetOwner() && GetOwner()->IsPlayer() )
- {
- CHL2MP_Player *pPlayer = assert_cast<CHL2MP_Player*>( GetOwner() );
- if ( pPlayer->IsWeaponLowered() )
- {
- if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) != ACTIVITY_NOT_AVAILABLE )
- {
- if ( DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_IDLE_LOWERED, (char*)GetAnimPrefix() ) )
- {
- m_bLowered = true;
-
- // Stomp the next attack time to fix the fact that the lower idles are long
- pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 );
- m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
- m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
- return true;
- }
- }
- }
- }
-
- m_bLowered = false;
- return BaseClass::Deploy();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseHL2MPCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo )
-{
- if ( BaseClass::Holster( pSwitchingTo ) )
- {
- SetWeaponVisible( false );
- m_flHolsterTime = gpGlobals->curtime;
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CBaseHL2MPCombatWeapon::WeaponShouldBeLowered( void )
-{
- // Can't be in the middle of another animation
- if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE &&
- GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE )
- return false;
-
- if ( m_bLowered )
- return true;
-
-#if !defined( CLIENT_DLL )
-
- if ( GlobalEntity_GetState( "friendly_encounter" ) == GLOBAL_ON )
- return true;
-
-#endif
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Allows the weapon to choose proper weapon idle animation
-//-----------------------------------------------------------------------------
-void CBaseHL2MPCombatWeapon::WeaponIdle( void )
-{
- //See if we should idle high or low
- if ( WeaponShouldBeLowered() )
- {
- // Move to lowered position if we're not there yet
- if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED
- && GetActivity() != ACT_TRANSITION )
- {
- SendWeaponAnim( ACT_VM_IDLE_LOWERED );
- }
- else if ( HasWeaponIdleTimeElapsed() )
- {
- // Keep idling low
- SendWeaponAnim( ACT_VM_IDLE_LOWERED );
- }
- }
- else
- {
- // See if we need to raise immediately
- if ( m_flRaiseTime < gpGlobals->curtime && GetActivity() == ACT_VM_IDLE_LOWERED )
- {
- SendWeaponAnim( ACT_VM_IDLE );
- }
- else if ( HasWeaponIdleTimeElapsed() )
- {
- SendWeaponAnim( ACT_VM_IDLE );
- }
- }
-}
-
-#if defined( CLIENT_DLL )
-
-#define HL2_BOB_CYCLE_MIN 1.0f
-#define HL2_BOB_CYCLE_MAX 0.45f
-#define HL2_BOB 0.002f
-#define HL2_BOB_UP 0.5f
-
-extern float g_lateralBob;
-extern float g_verticalBob;
-
-static ConVar cl_bobcycle( "cl_bobcycle","0.8" );
-static ConVar cl_bob( "cl_bob","0.002" );
-static ConVar cl_bobup( "cl_bobup","0.5" );
-
-// Register these cvars if needed for easy tweaking
-static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2", FCVAR_REPLICATED | FCVAR_CHEAT );
-static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5", FCVAR_REPLICATED | FCVAR_CHEAT );
-static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
-static ConVar v_iyaw_level( "v_iyaw_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT );
-static ConVar v_iroll_level( "v_iroll_level", "0.1", FCVAR_REPLICATED | FCVAR_CHEAT );
-static ConVar v_ipitch_level( "v_ipitch_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT );
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : float
-//-----------------------------------------------------------------------------
-float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void )
-{
- static float bobtime;
- static float lastbobtime;
- float cycle;
-
- CBasePlayer *player = ToBasePlayer( GetOwner() );
- //Assert( player );
-
- //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it
-
- if ( ( !gpGlobals->frametime ) || ( player == NULL ) )
- {
- //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
- return 0.0f;// just use old value
- }
-
- //Find the speed of the player
- float speed = player->GetLocalVelocity().Length2D();
-
- //FIXME: This maximum speed value must come from the server.
- // MaxSpeed() is not sufficient for dealing with sprinting - jdw
-
- speed = clamp( speed, -320, 320 );
-
- float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f );
-
- bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset;
- lastbobtime = gpGlobals->curtime;
-
- //Calculate the vertical bob
- cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX)*HL2_BOB_CYCLE_MAX;
- cycle /= HL2_BOB_CYCLE_MAX;
-
- if ( cycle < HL2_BOB_UP )
- {
- cycle = M_PI * cycle / HL2_BOB_UP;
- }
- else
- {
- cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP);
- }
-
- g_verticalBob = speed*0.005f;
- g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle);
-
- g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f );
-
- //Calculate the lateral bob
- cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX*2)*HL2_BOB_CYCLE_MAX*2;
- cycle /= HL2_BOB_CYCLE_MAX*2;
-
- if ( cycle < HL2_BOB_UP )
- {
- cycle = M_PI * cycle / HL2_BOB_UP;
- }
- else
- {
- cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP);
- }
-
- 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 );
-
- //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
- return 0.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &origin -
-// &angles -
-// viewmodelindex -
-//-----------------------------------------------------------------------------
-void CBaseHL2MPCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
-{
- Vector forward, right;
- AngleVectors( angles, &forward, &right, NULL );
-
- CalcViewmodelBob();
-
- // Apply bob, but scaled down to 40%
- VectorMA( origin, g_verticalBob * 0.1f, forward, origin );
-
- // Z bob a bit more
- origin[2] += g_verticalBob * 0.1f;
-
- // bob the angles
- angles[ ROLL ] += g_verticalBob * 0.5f;
- angles[ PITCH ] -= g_verticalBob * 0.4f;
-
- angles[ YAW ] -= g_lateralBob * 0.3f;
-
- VectorMA( origin, g_lateralBob * 0.8f, right, origin );
-}
-
-//-----------------------------------------------------------------------------
-Vector CBaseHL2MPCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency )
-{
- return BaseClass::GetBulletSpread( proficiency );
-}
-
-//-----------------------------------------------------------------------------
-float CBaseHL2MPCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency )
-{
- return BaseClass::GetSpreadBias( proficiency );
-}
-//-----------------------------------------------------------------------------
-
-const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetProficiencyValues()
-{
- return NULL;
-}
-
-#else
-
-// Server stubs
-float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void )
-{
- return 0.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &origin -
-// &angles -
-// viewmodelindex -
-//-----------------------------------------------------------------------------
-void CBaseHL2MPCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
-{
-}
-
-
-//-----------------------------------------------------------------------------
-Vector CBaseHL2MPCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency )
-{
- Vector baseSpread = BaseClass::GetBulletSpread( proficiency );
-
- const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues();
- float flModifier = (pProficiencyValues)[ proficiency ].spreadscale;
- return ( baseSpread * flModifier );
-}
-
-//-----------------------------------------------------------------------------
-float CBaseHL2MPCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency )
-{
- const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues();
- return (pProficiencyValues)[ proficiency ].bias;
-}
-
-//-----------------------------------------------------------------------------
-const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetProficiencyValues()
-{
- return GetDefaultProficiencyValues();
-}
-
-//-----------------------------------------------------------------------------
-const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetDefaultProficiencyValues()
-{
- // Weapon proficiency table. Keep this in sync with WeaponProficiency_t enum in the header!!
- static WeaponProficiencyInfo_t g_BaseWeaponProficiencyTable[] =
- {
- { 2.50, 1.0 },
- { 2.00, 1.0 },
- { 1.50, 1.0 },
- { 1.25, 1.0 },
- { 1.00, 1.0 },
- };
-
- COMPILE_TIME_ASSERT( ARRAYSIZE(g_BaseWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1);
-
- return g_BaseWeaponProficiencyTable;
-}
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+
+#include "hl2mp_player_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+LINK_ENTITY_TO_CLASS( basehl2mpcombatweapon, CBaseHL2MPCombatWeapon );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon )
+
+BEGIN_NETWORK_TABLE( CBaseHL2MPCombatWeapon , DT_BaseHL2MPCombatWeapon )
+#if !defined( CLIENT_DLL )
+// SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ),
+#else
+// RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ),
+#endif
+END_NETWORK_TABLE()
+
+
+#if !defined( CLIENT_DLL )
+
+#include "globalstate.h"
+
+//---------------------------------------------------------
+// Save/Restore
+//---------------------------------------------------------
+BEGIN_DATADESC( CBaseHL2MPCombatWeapon )
+
+ DEFINE_FIELD( m_bLowered, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flRaiseTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flHolsterTime, FIELD_TIME ),
+
+END_DATADESC()
+
+#endif
+
+BEGIN_PREDICTION_DATA( CBaseHL2MPCombatWeapon )
+END_PREDICTION_DATA()
+
+extern ConVar sk_auto_reload_time;
+
+CBaseHL2MPCombatWeapon::CBaseHL2MPCombatWeapon( void )
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseHL2MPCombatWeapon::ItemHolsterFrame( void )
+{
+ BaseClass::ItemHolsterFrame();
+
+ // Must be player held
+ if ( GetOwner() && GetOwner()->IsPlayer() == false )
+ return;
+
+ // We can't be active
+ if ( GetOwner()->GetActiveWeapon() == this )
+ return;
+
+ // If it's been longer than three seconds, reload
+ if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() )
+ {
+ // Just load the clip with no animations
+ FinishReload();
+ m_flHolsterTime = gpGlobals->curtime;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Drops the weapon into a lowered pose
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseHL2MPCombatWeapon::Lower( void )
+{
+ //Don't bother if we don't have the animation
+ if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) == ACTIVITY_NOT_AVAILABLE )
+ return false;
+
+ m_bLowered = true;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Brings the weapon up to the ready position
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseHL2MPCombatWeapon::Ready( void )
+{
+ //Don't bother if we don't have the animation
+ if ( SelectWeightedSequence( ACT_VM_LOWERED_TO_IDLE ) == ACTIVITY_NOT_AVAILABLE )
+ return false;
+
+ m_bLowered = false;
+ m_flRaiseTime = gpGlobals->curtime + 0.5f;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseHL2MPCombatWeapon::Deploy( void )
+{
+ // If we should be lowered, deploy in the lowered position
+ // We have to ask the player if the last time it checked, the weapon was lowered
+ if ( GetOwner() && GetOwner()->IsPlayer() )
+ {
+ CHL2MP_Player *pPlayer = assert_cast<CHL2MP_Player*>( GetOwner() );
+ if ( pPlayer->IsWeaponLowered() )
+ {
+ if ( SelectWeightedSequence( ACT_VM_IDLE_LOWERED ) != ACTIVITY_NOT_AVAILABLE )
+ {
+ if ( DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_IDLE_LOWERED, (char*)GetAnimPrefix() ) )
+ {
+ m_bLowered = true;
+
+ // Stomp the next attack time to fix the fact that the lower idles are long
+ pPlayer->SetNextAttack( gpGlobals->curtime + 1.0 );
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
+ return true;
+ }
+ }
+ }
+ }
+
+ m_bLowered = false;
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseHL2MPCombatWeapon::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ if ( BaseClass::Holster( pSwitchingTo ) )
+ {
+ SetWeaponVisible( false );
+ m_flHolsterTime = gpGlobals->curtime;
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseHL2MPCombatWeapon::WeaponShouldBeLowered( void )
+{
+ // Can't be in the middle of another animation
+ if ( GetIdealActivity() != ACT_VM_IDLE_LOWERED && GetIdealActivity() != ACT_VM_IDLE &&
+ GetIdealActivity() != ACT_VM_IDLE_TO_LOWERED && GetIdealActivity() != ACT_VM_LOWERED_TO_IDLE )
+ return false;
+
+ if ( m_bLowered )
+ return true;
+
+#if !defined( CLIENT_DLL )
+
+ if ( GlobalEntity_GetState( "friendly_encounter" ) == GLOBAL_ON )
+ return true;
+
+#endif
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows the weapon to choose proper weapon idle animation
+//-----------------------------------------------------------------------------
+void CBaseHL2MPCombatWeapon::WeaponIdle( void )
+{
+ //See if we should idle high or low
+ if ( WeaponShouldBeLowered() )
+ {
+ // Move to lowered position if we're not there yet
+ if ( GetActivity() != ACT_VM_IDLE_LOWERED && GetActivity() != ACT_VM_IDLE_TO_LOWERED
+ && GetActivity() != ACT_TRANSITION )
+ {
+ SendWeaponAnim( ACT_VM_IDLE_LOWERED );
+ }
+ else if ( HasWeaponIdleTimeElapsed() )
+ {
+ // Keep idling low
+ SendWeaponAnim( ACT_VM_IDLE_LOWERED );
+ }
+ }
+ else
+ {
+ // See if we need to raise immediately
+ if ( m_flRaiseTime < gpGlobals->curtime && GetActivity() == ACT_VM_IDLE_LOWERED )
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ else if ( HasWeaponIdleTimeElapsed() )
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ }
+}
+
+#if defined( CLIENT_DLL )
+
+#define HL2_BOB_CYCLE_MIN 1.0f
+#define HL2_BOB_CYCLE_MAX 0.45f
+#define HL2_BOB 0.002f
+#define HL2_BOB_UP 0.5f
+
+extern float g_lateralBob;
+extern float g_verticalBob;
+
+static ConVar cl_bobcycle( "cl_bobcycle","0.8" );
+static ConVar cl_bob( "cl_bob","0.002" );
+static ConVar cl_bobup( "cl_bobup","0.5" );
+
+// Register these cvars if needed for easy tweaking
+static ConVar v_iyaw_cycle( "v_iyaw_cycle", "2", FCVAR_REPLICATED | FCVAR_CHEAT );
+static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5", FCVAR_REPLICATED | FCVAR_CHEAT );
+static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
+static ConVar v_iyaw_level( "v_iyaw_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT );
+static ConVar v_iroll_level( "v_iroll_level", "0.1", FCVAR_REPLICATED | FCVAR_CHEAT );
+static ConVar v_ipitch_level( "v_ipitch_level", "0.3", FCVAR_REPLICATED | FCVAR_CHEAT );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void )
+{
+ static float bobtime;
+ static float lastbobtime;
+ float cycle;
+
+ CBasePlayer *player = ToBasePlayer( GetOwner() );
+ //Assert( player );
+
+ //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it
+
+ if ( ( !gpGlobals->frametime ) || ( player == NULL ) )
+ {
+ //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
+ return 0.0f;// just use old value
+ }
+
+ //Find the speed of the player
+ float speed = player->GetLocalVelocity().Length2D();
+
+ //FIXME: This maximum speed value must come from the server.
+ // MaxSpeed() is not sufficient for dealing with sprinting - jdw
+
+ speed = clamp( speed, -320, 320 );
+
+ float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f );
+
+ bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset;
+ lastbobtime = gpGlobals->curtime;
+
+ //Calculate the vertical bob
+ cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX)*HL2_BOB_CYCLE_MAX;
+ cycle /= HL2_BOB_CYCLE_MAX;
+
+ if ( cycle < HL2_BOB_UP )
+ {
+ cycle = M_PI * cycle / HL2_BOB_UP;
+ }
+ else
+ {
+ cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP);
+ }
+
+ g_verticalBob = speed*0.005f;
+ g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle);
+
+ g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f );
+
+ //Calculate the lateral bob
+ cycle = bobtime - (int)(bobtime/HL2_BOB_CYCLE_MAX*2)*HL2_BOB_CYCLE_MAX*2;
+ cycle /= HL2_BOB_CYCLE_MAX*2;
+
+ if ( cycle < HL2_BOB_UP )
+ {
+ cycle = M_PI * cycle / HL2_BOB_UP;
+ }
+ else
+ {
+ cycle = M_PI + M_PI*(cycle-HL2_BOB_UP)/(1.0 - HL2_BOB_UP);
+ }
+
+ 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 );
+
+ //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &origin -
+// &angles -
+// viewmodelindex -
+//-----------------------------------------------------------------------------
+void CBaseHL2MPCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
+{
+ Vector forward, right;
+ AngleVectors( angles, &forward, &right, NULL );
+
+ CalcViewmodelBob();
+
+ // Apply bob, but scaled down to 40%
+ VectorMA( origin, g_verticalBob * 0.1f, forward, origin );
+
+ // Z bob a bit more
+ origin[2] += g_verticalBob * 0.1f;
+
+ // bob the angles
+ angles[ ROLL ] += g_verticalBob * 0.5f;
+ angles[ PITCH ] -= g_verticalBob * 0.4f;
+
+ angles[ YAW ] -= g_lateralBob * 0.3f;
+
+ VectorMA( origin, g_lateralBob * 0.8f, right, origin );
+}
+
+//-----------------------------------------------------------------------------
+Vector CBaseHL2MPCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency )
+{
+ return BaseClass::GetBulletSpread( proficiency );
+}
+
+//-----------------------------------------------------------------------------
+float CBaseHL2MPCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency )
+{
+ return BaseClass::GetSpreadBias( proficiency );
+}
+//-----------------------------------------------------------------------------
+
+const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetProficiencyValues()
+{
+ return NULL;
+}
+
+#else
+
+// Server stubs
+float CBaseHL2MPCombatWeapon::CalcViewmodelBob( void )
+{
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &origin -
+// &angles -
+// viewmodelindex -
+//-----------------------------------------------------------------------------
+void CBaseHL2MPCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+Vector CBaseHL2MPCombatWeapon::GetBulletSpread( WeaponProficiency_t proficiency )
+{
+ Vector baseSpread = BaseClass::GetBulletSpread( proficiency );
+
+ const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues();
+ float flModifier = (pProficiencyValues)[ proficiency ].spreadscale;
+ return ( baseSpread * flModifier );
+}
+
+//-----------------------------------------------------------------------------
+float CBaseHL2MPCombatWeapon::GetSpreadBias( WeaponProficiency_t proficiency )
+{
+ const WeaponProficiencyInfo_t *pProficiencyValues = GetProficiencyValues();
+ return (pProficiencyValues)[ proficiency ].bias;
+}
+
+//-----------------------------------------------------------------------------
+const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetProficiencyValues()
+{
+ return GetDefaultProficiencyValues();
+}
+
+//-----------------------------------------------------------------------------
+const WeaponProficiencyInfo_t *CBaseHL2MPCombatWeapon::GetDefaultProficiencyValues()
+{
+ // Weapon proficiency table. Keep this in sync with WeaponProficiency_t enum in the header!!
+ static WeaponProficiencyInfo_t g_BaseWeaponProficiencyTable[] =
+ {
+ { 2.50, 1.0 },
+ { 2.00, 1.0 },
+ { 1.50, 1.0 },
+ { 1.25, 1.0 },
+ { 1.00, 1.0 },
+ };
+
+ COMPILE_TIME_ASSERT( ARRAYSIZE(g_BaseWeaponProficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1);
+
+ return g_BaseWeaponProficiencyTable;
+}
+
#endif \ No newline at end of file
diff --git a/mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h b/mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h
index 25ca520c..e8469326 100644
--- a/mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h
+++ b/mp/src/game/shared/hl2mp/weapon_hl2mpbasehlmpcombatweapon.h
@@ -1,63 +1,63 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-#ifndef WEAPON_BASEHL2MPCOMBATWEAPON_SHARED_H
-#define WEAPON_BASEHL2MPCOMBATWEAPON_SHARED_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
-#else
- #include "hl2mp_player.h"
-#endif
-
-#include "weapon_hl2mpbase.h"
-
-#if defined( CLIENT_DLL )
-#define CBaseHL2MPCombatWeapon C_BaseHL2MPCombatWeapon
-#endif
-
-class CBaseHL2MPCombatWeapon : public CWeaponHL2MPBase
-{
-#if !defined( CLIENT_DLL )
- DECLARE_DATADESC();
-#endif
-
- DECLARE_CLASS( CBaseHL2MPCombatWeapon, CWeaponHL2MPBase );
-public:
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- CBaseHL2MPCombatWeapon();
-
- virtual bool WeaponShouldBeLowered( void );
-
- virtual bool Ready( void );
- virtual bool Lower( void );
- virtual bool Deploy( void );
- virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
- virtual void WeaponIdle( void );
-
- virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles );
- virtual float CalcViewmodelBob( void );
-
- virtual Vector GetBulletSpread( WeaponProficiency_t proficiency );
- virtual float GetSpreadBias( WeaponProficiency_t proficiency );
-
- virtual const WeaponProficiencyInfo_t *GetProficiencyValues();
- static const WeaponProficiencyInfo_t *GetDefaultProficiencyValues();
-
- virtual void ItemHolsterFrame( void );
-
-protected:
-
- bool m_bLowered; // Whether the viewmodel is raised or lowered
- float m_flRaiseTime; // If lowered, the time we should raise the viewmodel
- float m_flHolsterTime; // When the weapon was holstered
-
-private:
-
- CBaseHL2MPCombatWeapon( const CBaseHL2MPCombatWeapon & );
-};
-
-#endif // WEAPON_BASEHL2MPCOMBATWEAPON_SHARED_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#ifndef WEAPON_BASEHL2MPCOMBATWEAPON_SHARED_H
+#define WEAPON_BASEHL2MPCOMBATWEAPON_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+#else
+ #include "hl2mp_player.h"
+#endif
+
+#include "weapon_hl2mpbase.h"
+
+#if defined( CLIENT_DLL )
+#define CBaseHL2MPCombatWeapon C_BaseHL2MPCombatWeapon
+#endif
+
+class CBaseHL2MPCombatWeapon : public CWeaponHL2MPBase
+{
+#if !defined( CLIENT_DLL )
+ DECLARE_DATADESC();
+#endif
+
+ DECLARE_CLASS( CBaseHL2MPCombatWeapon, CWeaponHL2MPBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CBaseHL2MPCombatWeapon();
+
+ virtual bool WeaponShouldBeLowered( void );
+
+ virtual bool Ready( void );
+ virtual bool Lower( void );
+ virtual bool Deploy( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual void WeaponIdle( void );
+
+ virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles );
+ virtual float CalcViewmodelBob( void );
+
+ virtual Vector GetBulletSpread( WeaponProficiency_t proficiency );
+ virtual float GetSpreadBias( WeaponProficiency_t proficiency );
+
+ virtual const WeaponProficiencyInfo_t *GetProficiencyValues();
+ static const WeaponProficiencyInfo_t *GetDefaultProficiencyValues();
+
+ virtual void ItemHolsterFrame( void );
+
+protected:
+
+ bool m_bLowered; // Whether the viewmodel is raised or lowered
+ float m_flRaiseTime; // If lowered, the time we should raise the viewmodel
+ float m_flHolsterTime; // When the weapon was holstered
+
+private:
+
+ CBaseHL2MPCombatWeapon( const CBaseHL2MPCombatWeapon & );
+};
+
+#endif // WEAPON_BASEHL2MPCOMBATWEAPON_SHARED_H
diff --git a/mp/src/game/shared/hl2mp/weapon_physcannon.cpp b/mp/src/game/shared/hl2mp/weapon_physcannon.cpp
index 97245b14..4972a1ed 100644
--- a/mp/src/game/shared/hl2mp/weapon_physcannon.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_physcannon.cpp
@@ -1,3685 +1,3685 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Physics cannon
-//
-//=============================================================================//
-
-#include "cbase.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
- #include "vcollide_parse.h"
- #include "engine/ivdebugoverlay.h"
- #include "iviewrender_beams.h"
- #include "beamdraw.h"
- #include "c_te_effect_dispatch.h"
- #include "model_types.h"
- #include "clienteffectprecachesystem.h"
- #include "fx_interpvalue.h"
-#else
- #include "hl2mp_player.h"
- #include "soundent.h"
- #include "ndebugoverlay.h"
- #include "ai_basenpc.h"
- #include "player_pickup.h"
- #include "physics_prop_ragdoll.h"
- #include "globalstate.h"
- #include "props.h"
- #include "te_effect_dispatch.h"
- #include "util.h"
-#endif
-
-#include "gamerules.h"
-#include "soundenvelope.h"
-#include "engine/IEngineSound.h"
-#include "physics.h"
-#include "in_buttons.h"
-#include "IEffects.h"
-#include "shake.h"
-#include "beam_shared.h"
-#include "Sprite.h"
-#include "weapon_physcannon.h"
-#include "physics_saverestore.h"
-#include "movevars_shared.h"
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-#include "vphysics/friction.h"
-#include "debugoverlay_shared.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define SPRITE_SCALE 128.0f
-
-static const char *s_pWaitForUpgradeContext = "WaitForUpgrade";
-
-ConVar g_debug_physcannon( "g_debug_physcannon", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
-
-ConVar physcannon_minforce( "physcannon_minforce", "700", FCVAR_REPLICATED | FCVAR_CHEAT );
-ConVar physcannon_maxforce( "physcannon_maxforce", "1500", FCVAR_REPLICATED | FCVAR_CHEAT );
-ConVar physcannon_maxmass( "physcannon_maxmass", "250", FCVAR_REPLICATED | FCVAR_CHEAT );
-ConVar physcannon_tracelength( "physcannon_tracelength", "250", FCVAR_REPLICATED | FCVAR_CHEAT );
-ConVar physcannon_chargetime("physcannon_chargetime", "2", FCVAR_REPLICATED | FCVAR_CHEAT );
-ConVar physcannon_pullforce( "physcannon_pullforce", "4000", FCVAR_REPLICATED | FCVAR_CHEAT );
-ConVar physcannon_cone( "physcannon_cone", "0.97", FCVAR_REPLICATED | FCVAR_CHEAT );
-ConVar physcannon_ball_cone( "physcannon_ball_cone", "0.997", FCVAR_REPLICATED | FCVAR_CHEAT );
-ConVar player_throwforce( "player_throwforce", "1000", FCVAR_REPLICATED | FCVAR_CHEAT );
-
-#ifndef CLIENT_DLL
-extern ConVar hl2_normspeed;
-extern ConVar hl2_walkspeed;
-#endif
-
-#define PHYSCANNON_BEAM_SPRITE "sprites/orangelight1.vmt"
-#define PHYSCANNON_BEAM_SPRITE_NOZ "sprites/orangelight1_noz.vmt"
-#define PHYSCANNON_GLOW_SPRITE "sprites/glow04_noz"
-#define PHYSCANNON_ENDCAP_SPRITE "sprites/orangeflare1"
-#define PHYSCANNON_CENTER_GLOW "sprites/orangecore1"
-#define PHYSCANNON_BLAST_SPRITE "sprites/orangecore2"
-
-#ifdef CLIENT_DLL
-
- //Precahce the effects
- CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectPhysCannon )
- CLIENTEFFECT_MATERIAL( "sprites/orangelight1" )
- CLIENTEFFECT_MATERIAL( "sprites/orangelight1_noz" )
- CLIENTEFFECT_MATERIAL( PHYSCANNON_GLOW_SPRITE )
- CLIENTEFFECT_MATERIAL( PHYSCANNON_ENDCAP_SPRITE )
- CLIENTEFFECT_MATERIAL( PHYSCANNON_CENTER_GLOW )
- CLIENTEFFECT_MATERIAL( PHYSCANNON_BLAST_SPRITE )
- CLIENTEFFECT_REGISTER_END()
-
-#endif // CLIENT_DLL
-
-#ifndef CLIENT_DLL
-
-void PhysCannonBeginUpgrade( CBaseAnimating *pAnim )
-{
-
-}
-
-bool PlayerHasMegaPhysCannon( void )
-{
- return false;
-}
-
-bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject )
-{
- // BRJ: FIXME! This can't be implemented trivially, so I'm leaving it to Steve or Adrian
- Assert( 0 );
- return false;
-}
-
-#endif
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-// this will hit skip the pass entity, but not anything it owns
-// (lets player grab own grenades)
-class CTraceFilterNoOwnerTest : public CTraceFilterSimple
-{
-public:
- DECLARE_CLASS( CTraceFilterNoOwnerTest, CTraceFilterSimple );
-
- CTraceFilterNoOwnerTest( const IHandleEntity *passentity, int collisionGroup )
- : CTraceFilterSimple( NULL, collisionGroup ), m_pPassNotOwner(passentity)
- {
- }
-
- virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
- {
- if ( pHandleEntity != m_pPassNotOwner )
- return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
-
- return false;
- }
-
-protected:
- const IHandleEntity *m_pPassNotOwner;
-};
-
-static void MatrixOrthogonalize( matrix3x4_t &matrix, int column )
-{
- Vector columns[3];
- int i;
-
- for ( i = 0; i < 3; i++ )
- {
- MatrixGetColumn( matrix, i, columns[i] );
- }
-
- int index0 = column;
- int index1 = (column+1)%3;
- int index2 = (column+2)%3;
-
- columns[index2] = CrossProduct( columns[index0], columns[index1] );
- columns[index1] = CrossProduct( columns[index2], columns[index0] );
- VectorNormalize( columns[index2] );
- VectorNormalize( columns[index1] );
- MatrixSetColumn( columns[index1], index1, matrix );
- MatrixSetColumn( columns[index2], index2, matrix );
-}
-
-#define SIGN(x) ( (x) < 0 ? -1 : 1 )
-
-static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle )
-{
- matrix3x4_t alignMatrix;
- AngleMatrix( angles, alignMatrix );
-
- // NOTE: Must align z first
- for ( int j = 3; --j >= 0; )
- {
- Vector vec;
- MatrixGetColumn( alignMatrix, j, vec );
- for ( int i = 0; i < 3; i++ )
- {
- if ( fabs(vec[i]) > cosineAlignAngle )
- {
- vec[i] = SIGN(vec[i]);
- vec[(i+1)%3] = 0;
- vec[(i+2)%3] = 0;
- MatrixSetColumn( vec, j, alignMatrix );
- MatrixOrthogonalize( alignMatrix, j );
- break;
- }
- }
- }
-
- QAngle out;
- MatrixAngles( alignMatrix, out );
- return out;
-}
-
-
-static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector &start, const Vector &end, const QAngle &angles, const Vector &boxOrigin, const Vector &mins, const Vector &maxs, trace_t *ptr )
-{
- physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr );
-
- if ( ptr->DidHit() )
- {
- ptr->endpos = start * (1-ptr->fraction) + end * ptr->fraction;
- ptr->startpos = start;
- ptr->plane.dist = -ptr->plane.dist;
- ptr->plane.normal *= -1;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Computes a local matrix for the player clamped to valid carry ranges
-//-----------------------------------------------------------------------------
-// when looking level, hold bottom of object 8 inches below eye level
-#define PLAYER_HOLD_LEVEL_EYES -8
-
-// when looking down, hold bottom of object 0 inches from feet
-#define PLAYER_HOLD_DOWN_FEET 2
-
-// when looking up, hold bottom of object 24 inches above eye level
-#define PLAYER_HOLD_UP_EYES 24
-
-// use a +/-30 degree range for the entire range of motion of pitch
-#define PLAYER_LOOK_PITCH_RANGE 30
-
-// player can reach down 2ft below his feet (otherwise he'll hold the object above the bottom)
-#define PLAYER_REACH_DOWN_DISTANCE 24
-
-static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out )
-{
- if ( !pPlayer )
- return;
-
- QAngle angles = pPlayer->EyeAngles();
- Vector origin = pPlayer->EyePosition();
-
- // 0-360 / -180-180
- //angles.x = init ? 0 : AngleDistance( angles.x, 0 );
- //angles.x = clamp( angles.x, -PLAYER_LOOK_PITCH_RANGE, PLAYER_LOOK_PITCH_RANGE );
- angles.x = 0;
-
- float feet = pPlayer->GetAbsOrigin().z + pPlayer->WorldAlignMins().z;
- float eyes = origin.z;
- float zoffset = 0;
- // moving up (negative pitch is up)
- if ( angles.x < 0 )
- {
- zoffset = RemapVal( angles.x, 0, -PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_UP_EYES );
- }
- else
- {
- zoffset = RemapVal( angles.x, 0, PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_DOWN_FEET + (feet - eyes) );
- }
- origin.z += zoffset;
- angles.x = 0;
- AngleMatrix( angles, origin, out );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-
-// derive from this so we can add save/load data to it
-struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t
-{
- DECLARE_SIMPLE_DATADESC();
-};
-
-BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t )
-
- DEFINE_FIELD( targetPosition, FIELD_POSITION_VECTOR ),
- DEFINE_FIELD( targetRotation, FIELD_VECTOR ),
- DEFINE_FIELD( maxAngular, FIELD_FLOAT ),
- DEFINE_FIELD( maxDampAngular, FIELD_FLOAT ),
- DEFINE_FIELD( maxSpeed, FIELD_FLOAT ),
- DEFINE_FIELD( maxDampSpeed, FIELD_FLOAT ),
- DEFINE_FIELD( dampFactor, FIELD_FLOAT ),
- DEFINE_FIELD( teleportDistance, FIELD_FLOAT ),
-
-END_DATADESC()
-
-//-----------------------------------------------------------------------------
-class CGrabController : public IMotionEvent
-{
-public:
-
- CGrabController( void );
- ~CGrabController( void );
- void AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition );
- void DetachEntity( bool bClearVelocity );
- void OnRestore();
-
- bool UpdateObject( CBasePlayer *pPlayer, float flError );
-
- void SetTargetPosition( const Vector &target, const QAngle &targetOrientation );
- float ComputeError();
- float GetLoadWeight( void ) const { return m_flLoadWeight; }
- void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; }
- void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; }
- QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
- QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
-
- CBaseEntity *GetAttached() { return (CBaseEntity *)m_attachedEntity; }
-
- IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
- float GetSavedMass( IPhysicsObject *pObject );
-
- QAngle m_attachedAnglesPlayerSpace;
- Vector m_attachedPositionObjectSpace;
-
-private:
- // Compute the max speed for an attached object
- void ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics );
-
- game_shadowcontrol_params_t m_shadow;
- float m_timeToArrive;
- float m_errorTime;
- float m_error;
- float m_contactAmount;
- float m_angleAlignment;
- bool m_bCarriedEntityBlocksLOS;
- bool m_bIgnoreRelativePitch;
-
- float m_flLoadWeight;
- float m_savedRotDamping[VPHYSICS_MAX_OBJECT_LIST_COUNT];
- float m_savedMass[VPHYSICS_MAX_OBJECT_LIST_COUNT];
- EHANDLE m_attachedEntity;
- QAngle m_vecPreferredCarryAngles;
- bool m_bHasPreferredCarryAngles;
-
-
- IPhysicsMotionController *m_controller;
- int m_frameCount;
- friend class CWeaponPhysCannon;
-};
-
-const float DEFAULT_MAX_ANGULAR = 360.0f * 10.0f;
-const float REDUCED_CARRY_MASS = 1.0f;
-
-CGrabController::CGrabController( void )
-{
- m_shadow.dampFactor = 1.0;
- m_shadow.teleportDistance = 0;
- m_errorTime = 0;
- m_error = 0;
- // make this controller really stiff!
- m_shadow.maxSpeed = 1000;
- m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
- m_shadow.maxDampSpeed = m_shadow.maxSpeed*2;
- m_shadow.maxDampAngular = m_shadow.maxAngular;
- m_attachedEntity = NULL;
- m_vecPreferredCarryAngles = vec3_angle;
- m_bHasPreferredCarryAngles = false;
-}
-
-CGrabController::~CGrabController( void )
-{
- DetachEntity( false );
-}
-
-void CGrabController::OnRestore()
-{
- if ( m_controller )
- {
- m_controller->SetEventHandler( this );
- }
-}
-
-void CGrabController::SetTargetPosition( const Vector &target, const QAngle &targetOrientation )
-{
- m_shadow.targetPosition = target;
- m_shadow.targetRotation = targetOrientation;
-
- m_timeToArrive = gpGlobals->frametime;
-
- CBaseEntity *pAttached = GetAttached();
- if ( pAttached )
- {
- IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
-
- if ( pObj != NULL )
- {
- pObj->Wake();
- }
- else
- {
- DetachEntity( false );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : float
-//-----------------------------------------------------------------------------
-float CGrabController::ComputeError()
-{
- if ( m_errorTime <= 0 )
- return 0;
-
- CBaseEntity *pAttached = GetAttached();
- if ( pAttached )
- {
- Vector pos;
- IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
-
- if ( pObj )
- {
- pObj->GetShadowPosition( &pos, NULL );
-
- float error = (m_shadow.targetPosition - pos).Length();
- if ( m_errorTime > 0 )
- {
- if ( m_errorTime > 1 )
- {
- m_errorTime = 1;
- }
- float speed = error / m_errorTime;
- if ( speed > m_shadow.maxSpeed )
- {
- error *= 0.5;
- }
- m_error = (1-m_errorTime) * m_error + error * m_errorTime;
- }
- }
- else
- {
- DevMsg( "Object attached to Physcannon has no physics object\n" );
- DetachEntity( false );
- return 9999; // force detach
- }
- }
-
- if ( pAttached->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
- {
- m_error *= 3.0f;
- }
-
- m_errorTime = 0;
-
- return m_error;
-}
-
-
-#define MASS_SPEED_SCALE 60
-#define MAX_MASS 40
-
-void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics )
-{
-#ifndef CLIENT_DLL
- m_shadow.maxSpeed = 1000;
- m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
-
- // Compute total mass...
- float flMass = PhysGetEntityMass( pEntity );
- float flMaxMass = physcannon_maxmass.GetFloat();
- if ( flMass <= flMaxMass )
- return;
-
- float flLerpFactor = clamp( flMass, flMaxMass, 500.0f );
- flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f );
-
- float invMass = pPhysics->GetInvMass();
- float invInertia = pPhysics->GetInvInertia().Length();
-
- float invMaxMass = 1.0f / MAX_MASS;
- float ratio = invMaxMass / invMass;
- invMass = invMaxMass;
- invInertia *= ratio;
-
- float maxSpeed = invMass * MASS_SPEED_SCALE * 200;
- float maxAngular = invInertia * MASS_SPEED_SCALE * 360;
-
- m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed );
- m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular );
-#endif
-}
-
-
-QAngle CGrabController::TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
-{
- if ( m_bIgnoreRelativePitch )
- {
- matrix3x4_t test;
- QAngle angleTest = pPlayer->EyeAngles();
- angleTest.x = 0;
- AngleMatrix( angleTest, test );
- return TransformAnglesToLocalSpace( anglesIn, test );
- }
- return TransformAnglesToLocalSpace( anglesIn, pPlayer->EntityToWorldTransform() );
-}
-
-QAngle CGrabController::TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
-{
- if ( m_bIgnoreRelativePitch )
- {
- matrix3x4_t test;
- QAngle angleTest = pPlayer->EyeAngles();
- angleTest.x = 0;
- AngleMatrix( angleTest, test );
- return TransformAnglesToWorldSpace( anglesIn, test );
- }
- return TransformAnglesToWorldSpace( anglesIn, pPlayer->EntityToWorldTransform() );
-}
-
-
-void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition )
-{
- // play the impact sound of the object hitting the player
- // used as feedback to let the player know he picked up the object
-#ifndef CLIENT_DLL
- PhysicsImpactSound( pPlayer, pPhys, CHAN_STATIC, pPhys->GetMaterialIndex(), pPlayer->VPhysicsGetObject()->GetMaterialIndex(), 1.0, 64 );
-#endif
- Vector position;
- QAngle angles;
- pPhys->GetPosition( &position, &angles );
- // If it has a preferred orientation, use that instead.
-#ifndef CLIENT_DLL
- Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
-#endif
-
-// ComputeMaxSpeed( pEntity, pPhys );
-
- // Carried entities can never block LOS
- m_bCarriedEntityBlocksLOS = pEntity->BlocksLOS();
- pEntity->SetBlocksLOS( false );
- m_controller = physenv->CreateMotionController( this );
- m_controller->AttachObject( pPhys, true );
- // Don't do this, it's causing trouble with constraint solvers.
- //m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY );
-
- pPhys->Wake();
- PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
- SetTargetPosition( position, angles );
- m_attachedEntity = pEntity;
- IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
- int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
- m_flLoadWeight = 0;
- float damping = 10;
- float flFactor = count / 7.5f;
- if ( flFactor < 1.0f )
- {
- flFactor = 1.0f;
- }
- for ( int i = 0; i < count; i++ )
- {
- float mass = pList[i]->GetMass();
- pList[i]->GetDamping( NULL, &m_savedRotDamping[i] );
- m_flLoadWeight += mass;
- m_savedMass[i] = mass;
-
- // reduce the mass to prevent the player from adding crazy amounts of energy to the system
- pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor );
- pList[i]->SetDamping( NULL, &damping );
- }
-
- // Give extra mass to the phys object we're actually picking up
- pPhys->SetMass( REDUCED_CARRY_MASS );
- pPhys->EnableDrag( false );
-
- m_errorTime = -1.0f; // 1 seconds until error starts accumulating
- m_error = 0;
- m_contactAmount = 0;
-
- m_attachedAnglesPlayerSpace = TransformAnglesToPlayerSpace( angles, pPlayer );
- if ( m_angleAlignment != 0 )
- {
- m_attachedAnglesPlayerSpace = AlignAngles( m_attachedAnglesPlayerSpace, m_angleAlignment );
- }
-
- VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace );
-
-#ifndef CLIENT_DLL
- // If it's a prop, see if it has desired carry angles
- CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pEntity);
- if ( pProp )
- {
- m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles );
- }
- else
- {
- m_bHasPreferredCarryAngles = false;
- }
-#else
-
- m_bHasPreferredCarryAngles = false;
-#endif
-
-}
-
-static void ClampPhysicsVelocity( IPhysicsObject *pPhys, float linearLimit, float angularLimit )
-{
- Vector vel;
- AngularImpulse angVel;
- pPhys->GetVelocity( &vel, &angVel );
- float speed = VectorNormalize(vel) - linearLimit;
- float angSpeed = VectorNormalize(angVel) - angularLimit;
- speed = speed < 0 ? 0 : -speed;
- angSpeed = angSpeed < 0 ? 0 : -angSpeed;
- vel *= speed;
- angVel *= angSpeed;
- pPhys->AddVelocity( &vel, &angVel );
-}
-
-void CGrabController::DetachEntity( bool bClearVelocity )
-{
- CBaseEntity *pEntity = GetAttached();
- if ( pEntity )
- {
- // Restore the LS blocking state
- pEntity->SetBlocksLOS( m_bCarriedEntityBlocksLOS );
- IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
- int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
-
- for ( int i = 0; i < count; i++ )
- {
- IPhysicsObject *pPhys = pList[i];
- if ( !pPhys )
- continue;
-
- // on the odd chance that it's gone to sleep while under anti-gravity
- pPhys->EnableDrag( true );
- pPhys->Wake();
- pPhys->SetMass( m_savedMass[i] );
- pPhys->SetDamping( NULL, &m_savedRotDamping[i] );
- PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
- if ( bClearVelocity )
- {
- PhysForceClearVelocity( pPhys );
- }
- else
- {
-#ifndef CLIENT_DLL
- ClampPhysicsVelocity( pPhys, hl2_normspeed.GetFloat() * 1.5f, 2.0f * 360.0f );
-#endif
- }
-
- }
- }
-
- m_attachedEntity = NULL;
- if ( physenv )
- {
- physenv->DestroyMotionController( m_controller );
- }
- m_controller = NULL;
-}
-
-static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass )
-{
- bool contact = false;
- IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
- while ( pSnapshot->IsValid() )
- {
- IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
- if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass )
- {
- contact = true;
- break;
- }
- pSnapshot->NextFrictionData();
- }
- pObject->DestroyFrictionSnapshot( pSnapshot );
- return contact;
-}
-
-IMotionEvent::simresult_e CGrabController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
-{
- game_shadowcontrol_params_t shadowParams = m_shadow;
- if ( InContactWithHeavyObject( pObject, GetLoadWeight() ) )
- {
- m_contactAmount = Approach( 0.1f, m_contactAmount, deltaTime*2.0f );
- }
- else
- {
- m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime*2.0f );
- }
- shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount;
-#ifndef CLIENT_DLL
- m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime );
-#else
- m_timeToArrive = pObject->ComputeShadowControl( shadowParams, (TICK_INTERVAL*2), deltaTime );
-#endif
-
- // Slide along the current contact points to fix bouncing problems
- Vector velocity;
- AngularImpulse angVel;
- pObject->GetVelocity( &velocity, &angVel );
- PhysComputeSlideDirection( pObject, velocity, angVel, &velocity, &angVel, GetLoadWeight() );
- pObject->SetVelocityInstantaneous( &velocity, NULL );
-
- linear.Init();
- angular.Init();
- m_errorTime += deltaTime;
-
- return SIM_LOCAL_ACCELERATION;
-}
-
-float CGrabController::GetSavedMass( IPhysicsObject *pObject )
-{
- CBaseEntity *pHeld = m_attachedEntity;
- if ( pHeld )
- {
- if ( pObject->GetGameData() == (void*)pHeld )
- {
- IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
- int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
- for ( int i = 0; i < count; i++ )
- {
- if ( pList[i] == pObject )
- return m_savedMass[i];
- }
- }
- }
- return 0.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Player pickup controller
-//-----------------------------------------------------------------------------
-
-class CPlayerPickupController : public CBaseEntity
-{
- DECLARE_CLASS( CPlayerPickupController, CBaseEntity );
-public:
- void Init( CBasePlayer *pPlayer, CBaseEntity *pObject );
- void Shutdown( bool bThrown = false );
- bool OnControls( CBaseEntity *pControls ) { return true; }
- void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
- void OnRestore()
- {
- m_grabController.OnRestore();
- }
- void VPhysicsUpdate( IPhysicsObject *pPhysics ){}
- void VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) {}
-
- bool IsHoldingEntity( CBaseEntity *pEnt );
- CGrabController &GetGrabController() { return m_grabController; }
-
-private:
- CGrabController m_grabController;
- CBasePlayer *m_pPlayer;
-};
-
-LINK_ENTITY_TO_CLASS( player_pickup, CPlayerPickupController );
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pPlayer -
-// *pObject -
-//-----------------------------------------------------------------------------
-void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject )
-{
-#ifndef CLIENT_DLL
- // Holster player's weapon
- if ( pPlayer->GetActiveWeapon() )
- {
- if ( !pPlayer->GetActiveWeapon()->Holster() )
- {
- Shutdown();
- return;
- }
- }
-
-
- CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( pPlayer );
- if ( pOwner )
- {
- pOwner->EnableSprint( false );
- }
-
- // If the target is debris, convert it to non-debris
- if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
- {
- // Interactive debris converts back to debris when it comes to rest
- pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
- }
-
- // done so I'll go across level transitions with the player
- SetParent( pPlayer );
- m_grabController.SetIgnorePitch( true );
- m_grabController.SetAngleAlignment( DOT_30DEGREE );
- m_pPlayer = pPlayer;
- IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
- Pickup_OnPhysGunPickup( pObject, m_pPlayer );
-
- m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false );
-
- m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION;
- m_pPlayer->SetUseEntity( this );
-#endif
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : bool -
-//-----------------------------------------------------------------------------
-void CPlayerPickupController::Shutdown( bool bThrown )
-{
-#ifndef CLIENT_DLL
- CBaseEntity *pObject = m_grabController.GetAttached();
-
- bool bClearVelocity = false;
- if ( !bThrown && pObject && pObject->VPhysicsGetObject() && pObject->VPhysicsGetObject()->GetContactPoint(NULL,NULL) )
- {
- bClearVelocity = true;
- }
-
- m_grabController.DetachEntity( bClearVelocity );
-
- if ( pObject != NULL )
- {
- Pickup_OnPhysGunDrop( pObject, m_pPlayer, bThrown ? THROWN_BY_PLAYER : DROPPED_BY_PLAYER );
- }
-
- if ( m_pPlayer )
- {
- CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( m_pPlayer );
- if ( pOwner )
- {
- pOwner->EnableSprint( true );
- }
-
- m_pPlayer->SetUseEntity( NULL );
- if ( m_pPlayer->GetActiveWeapon() )
- {
- if ( !m_pPlayer->GetActiveWeapon()->Deploy() )
- {
- // We tried to restore the player's weapon, but we couldn't.
- // This usually happens when they're holding an empty weapon that doesn't
- // autoswitch away when out of ammo. Switch to next best weapon.
- m_pPlayer->SwitchToNextBestWeapon( NULL );
- }
- }
-
- m_pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION;
- }
- Remove();
-
-#endif
-
-}
-
-
-void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
-{
- if ( ToBasePlayer(pActivator) == m_pPlayer )
- {
- CBaseEntity *pAttached = m_grabController.GetAttached();
-
- // UNDONE: Use vphysics stress to decide to drop objects
- // UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work
- if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 )
- {
- Shutdown();
- return;
- }
-
- //Adrian: Oops, our object became motion disabled, let go!
- IPhysicsObject *pPhys = pAttached->VPhysicsGetObject();
- if ( pPhys && pPhys->IsMoveable() == false )
- {
- Shutdown();
- return;
- }
-
-#if STRESS_TEST
- vphysics_objectstress_t stress;
- CalculateObjectStress( pPhys, pAttached, &stress );
- if ( stress.exertedStress > 250 )
- {
- Shutdown();
- return;
- }
-#endif
- // +ATTACK will throw phys objects
- if ( m_pPlayer->m_nButtons & IN_ATTACK )
- {
- Shutdown( true );
- Vector vecLaunch;
- m_pPlayer->EyeVectors( &vecLaunch );
- // JAY: Scale this with mass because some small objects really go flying
- float massFactor = clamp( pPhys->GetMass(), 0.5, 15 );
- massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 );
- vecLaunch *= player_throwforce.GetFloat() * massFactor;
-
- pPhys->ApplyForceCenter( vecLaunch );
- AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor;
- pPhys->ApplyTorqueCenter( aVel );
- return;
- }
-
- if ( useType == USE_SET )
- {
- // update position
- m_grabController.UpdateObject( m_pPlayer, 12 );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pEnt -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CPlayerPickupController::IsHoldingEntity( CBaseEntity *pEnt )
-{
- return ( m_grabController.GetAttached() == pEnt );
-}
-
-void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject )
-{
-
-#ifndef CLIENT_DLL
-
- //Don't pick up if we don't have a phys object.
- if ( pObject->VPhysicsGetObject() == NULL )
- return;
-
- CPlayerPickupController *pController = (CPlayerPickupController *)CBaseEntity::Create( "player_pickup", pObject->GetAbsOrigin(), vec3_angle, pPlayer );
-
- if ( !pController )
- return;
-
- pController->Init( pPlayer, pObject );
-
-#endif
-
-}
-
-//----------------------------------------------------------------------------------------------------------------------------------------------------------
-// CInterpolatedValue class
-//----------------------------------------------------------------------------------------------------------------------------------------------------------
-
-#ifdef CLIENT_DLL
-
-//----------------------------------------------------------------------------------------------------------------------------------------------------------
-// CPhysCannonEffect class
-//----------------------------------------------------------------------------------------------------------------------------------------------------------
-
-class CPhysCannonEffect
-{
-public:
- CPhysCannonEffect( void ) : m_vecColor( 255, 255, 255 ), m_bVisible( true ), m_nAttachment( -1 ) {};
-
- void SetAttachment( int attachment ) { m_nAttachment = attachment; }
- int GetAttachment( void ) const { return m_nAttachment; }
-
- void SetVisible( bool visible = true ) { m_bVisible = visible; }
- int IsVisible( void ) const { return m_bVisible; }
-
- void SetColor( const Vector &color ) { m_vecColor = color; }
- const Vector &GetColor( void ) const { return m_vecColor; }
-
- bool SetMaterial( const char *materialName )
- {
- m_hMaterial.Init( materialName, TEXTURE_GROUP_CLIENT_EFFECTS );
- return ( m_hMaterial != NULL );
- }
-
- CMaterialReference &GetMaterial( void ) { return m_hMaterial; }
-
- CInterpolatedValue &GetAlpha( void ) { return m_Alpha; }
- CInterpolatedValue &GetScale( void ) { return m_Scale; }
-
-private:
- CInterpolatedValue m_Alpha;
- CInterpolatedValue m_Scale;
-
- Vector m_vecColor;
- bool m_bVisible;
- int m_nAttachment;
- CMaterialReference m_hMaterial;
-};
-
-//----------------------------------------------------------------------------------------------------------------------------------------------------------
-// CPhysCannonEffectBeam class
-//----------------------------------------------------------------------------------------------------------------------------------------------------------
-
-class CPhysCannonEffectBeam
-{
-public:
- CPhysCannonEffectBeam( void ) : m_pBeam( NULL ) {};
-
- ~CPhysCannonEffectBeam( void )
- {
- Release();
- }
-
- void Release( void )
- {
- if ( m_pBeam != NULL )
- {
- m_pBeam->flags = 0;
- m_pBeam->die = gpGlobals->curtime - 1;
-
- m_pBeam = NULL;
- }
- }
-
- void Init( int startAttachment, int endAttachment, CBaseEntity *pEntity, bool firstPerson )
- {
- if ( m_pBeam != NULL )
- return;
-
- BeamInfo_t beamInfo;
-
- beamInfo.m_pStartEnt = pEntity;
- beamInfo.m_nStartAttachment = startAttachment;
- beamInfo.m_pEndEnt = pEntity;
- beamInfo.m_nEndAttachment = endAttachment;
- beamInfo.m_nType = TE_BEAMPOINTS;
- beamInfo.m_vecStart = vec3_origin;
- beamInfo.m_vecEnd = vec3_origin;
-
- beamInfo.m_pszModelName = ( firstPerson ) ? PHYSCANNON_BEAM_SPRITE_NOZ : PHYSCANNON_BEAM_SPRITE;
-
- beamInfo.m_flHaloScale = 0.0f;
- beamInfo.m_flLife = 0.0f;
-
- if ( firstPerson )
- {
- beamInfo.m_flWidth = 0.0f;
- beamInfo.m_flEndWidth = 4.0f;
- }
- else
- {
- beamInfo.m_flWidth = 0.5f;
- beamInfo.m_flEndWidth = 2.0f;
- }
-
- beamInfo.m_flFadeLength = 0.0f;
- beamInfo.m_flAmplitude = 16;
- beamInfo.m_flBrightness = 255.0;
- beamInfo.m_flSpeed = 150.0f;
- beamInfo.m_nStartFrame = 0.0;
- beamInfo.m_flFrameRate = 30.0;
- beamInfo.m_flRed = 255.0;
- beamInfo.m_flGreen = 255.0;
- beamInfo.m_flBlue = 255.0;
- beamInfo.m_nSegments = 8;
- beamInfo.m_bRenderable = true;
- beamInfo.m_nFlags = FBEAM_FOREVER;
-
- m_pBeam = beams->CreateBeamEntPoint( beamInfo );
- }
-
- void SetVisible( bool state = true )
- {
- if ( m_pBeam == NULL )
- return;
-
- m_pBeam->brightness = ( state ) ? 255.0f : 0.0f;
- }
-
-private:
- Beam_t *m_pBeam;
-};
-
-#endif
-
-//----------------------------------------------------------------------------------------------------------------------------------------------------------
-// CWeaponPhysCannon class
-//----------------------------------------------------------------------------------------------------------------------------------------------------------
-
-#ifdef CLIENT_DLL
-#define CWeaponPhysCannon C_WeaponPhysCannon
-#endif
-
-class CWeaponPhysCannon : public CBaseHL2MPCombatWeapon
-{
-public:
- DECLARE_CLASS( CWeaponPhysCannon, CBaseHL2MPCombatWeapon );
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- CWeaponPhysCannon( void );
-
- void Drop( const Vector &vecVelocity );
- void Precache();
- virtual void OnRestore();
- virtual void StopLoopingSounds();
- virtual void UpdateOnRemove(void);
- void PrimaryAttack();
- void SecondaryAttack();
- void WeaponIdle();
- void ItemPreFrame();
- void ItemPostFrame();
-
- void ForceDrop( void );
- bool DropIfEntityHeld( CBaseEntity *pTarget ); // Drops its held entity if it matches the entity passed in
- CGrabController &GetGrabController() { return m_grabController; }
-
- bool CanHolster( void );
- bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
- bool Deploy( void );
-
- bool HasAnyAmmo( void ) { return true; }
-
- virtual void SetViewModel( void );
- virtual const char *GetShootSound( int iIndex ) const;
-
-#ifndef CLIENT_DLL
- CNetworkQAngle ( m_attachedAnglesPlayerSpace );
-#else
- QAngle m_attachedAnglesPlayerSpace;
-#endif
-
- CNetworkVector ( m_attachedPositionObjectSpace );
-
- CNetworkHandle( CBaseEntity, m_hAttachedObject );
-
- EHANDLE m_hOldAttachedObject;
-
-protected:
- enum FindObjectResult_t
- {
- OBJECT_FOUND = 0,
- OBJECT_NOT_FOUND,
- OBJECT_BEING_DETACHED,
- };
-
- void DoEffect( int effectType, Vector *pos = NULL );
-
- void OpenElements( void );
- void CloseElements( void );
-
- // Pickup and throw objects.
- bool CanPickupObject( CBaseEntity *pTarget );
- void CheckForTarget( void );
-
-#ifndef CLIENT_DLL
- bool AttachObject( CBaseEntity *pObject, const Vector &vPosition );
- FindObjectResult_t FindObject( void );
- CBaseEntity *FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone );
-#endif // !CLIENT_DLL
-
- void UpdateObject( void );
- void DetachObject( bool playSound = true, bool wasLaunched = false );
- void LaunchObject( const Vector &vecDir, float flForce );
- void StartEffects( void ); // Initialize all sprites and beams
- void StopEffects( bool stopSound = true ); // Hide all effects temporarily
- void DestroyEffects( void ); // Destroy all sprites and beams
-
- // Punt objects - this is pointing at an object in the world and applying a force to it.
- void PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
- void PuntVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
-
- // Velocity-based throw common to punt and launch code.
- void ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward );
-
- // Physgun effects
- void DoEffectClosed( void );
- void DoEffectReady( void );
- void DoEffectHolding( void );
- void DoEffectLaunch( Vector *pos );
- void DoEffectNone( void );
- void DoEffectIdle( void );
-
- // Trace length
- float TraceLength();
-
- // Sprite scale factor
- float SpriteScaleFactor();
-
- float GetLoadPercentage();
- CSoundPatch *GetMotorSound( void );
-
- void DryFire( void );
- void PrimaryFireEffect( void );
-
-#ifndef CLIENT_DLL
- // What happens when the physgun picks up something
- void Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason );
-#endif // !CLIENT_DLL
-
-#ifdef CLIENT_DLL
-
- enum EffectType_t
- {
- PHYSCANNON_CORE = 0,
-
- PHYSCANNON_BLAST,
-
- PHYSCANNON_GLOW1, // Must be in order!
- PHYSCANNON_GLOW2,
- PHYSCANNON_GLOW3,
- PHYSCANNON_GLOW4,
- PHYSCANNON_GLOW5,
- PHYSCANNON_GLOW6,
-
- PHYSCANNON_ENDCAP1, // Must be in order!
- PHYSCANNON_ENDCAP2,
- PHYSCANNON_ENDCAP3, // Only used in third-person!
-
- NUM_PHYSCANNON_PARAMETERS // Must be last!
- };
-
-#define NUM_GLOW_SPRITES ((CWeaponPhysCannon::PHYSCANNON_GLOW6-CWeaponPhysCannon::PHYSCANNON_GLOW1)+1)
-#define NUM_ENDCAP_SPRITES ((CWeaponPhysCannon::PHYSCANNON_ENDCAP3-CWeaponPhysCannon::PHYSCANNON_ENDCAP1)+1)
-
-#define NUM_PHYSCANNON_BEAMS 3
-
- virtual int DrawModel( int flags );
- virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
- virtual bool IsTransparent( void );
- virtual void OnDataChanged( DataUpdateType_t type );
- virtual void ClientThink( void );
-
- void ManagePredictedObject( void );
- void DrawEffects( void );
- void GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment );
- void DrawEffectSprite( EffectType_t effectID );
- inline bool IsEffectVisible( EffectType_t effectID );
- void UpdateElementPosition( void );
-
- // We need to render opaque and translucent pieces
- RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TWOPASS; }
-
- CInterpolatedValue m_ElementParameter; // Used to interpolate the position of the articulated elements
- CPhysCannonEffect m_Parameters[NUM_PHYSCANNON_PARAMETERS]; // Interpolated parameters for the effects
- CPhysCannonEffectBeam m_Beams[NUM_PHYSCANNON_BEAMS]; // Beams
-
- int m_nOldEffectState; // Used for parity checks
- bool m_bOldOpen; // Used for parity checks
-
- void NotifyShouldTransmit( ShouldTransmitState_t state );
-
-#endif // CLIENT_DLL
-
- int m_nChangeState; // For delayed state change of elements
- float m_flCheckSuppressTime; // Amount of time to suppress the checking for targets
- bool m_flLastDenySoundPlayed; // Debounce for deny sound
- int m_nAttack2Debounce;
-
- CNetworkVar( bool, m_bActive );
- CNetworkVar( int, m_EffectState ); // Current state of the effects on the gun
- CNetworkVar( bool, m_bOpen );
-
- bool m_bResetOwnerEntity;
-
- float m_flElementDebounce;
-
- CSoundPatch *m_sndMotor; // Whirring sound for the gun
-
- CGrabController m_grabController;
-
- float m_flRepuntObjectTime;
- EHANDLE m_hLastPuntedObject;
-
-private:
- CWeaponPhysCannon( const CWeaponPhysCannon & );
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-};
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPhysCannon, DT_WeaponPhysCannon )
-
-BEGIN_NETWORK_TABLE( CWeaponPhysCannon, DT_WeaponPhysCannon )
-#ifdef CLIENT_DLL
- RecvPropBool( RECVINFO( m_bActive ) ),
- RecvPropEHandle( RECVINFO( m_hAttachedObject ) ),
- RecvPropVector( RECVINFO( m_attachedPositionObjectSpace ) ),
- RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[0] ) ),
- RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[1] ) ),
- RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[2] ) ),
- RecvPropInt( RECVINFO( m_EffectState ) ),
- RecvPropBool( RECVINFO( m_bOpen ) ),
-#else
- SendPropBool( SENDINFO( m_bActive ) ),
- SendPropEHandle( SENDINFO( m_hAttachedObject ) ),
- SendPropVector(SENDINFO( m_attachedPositionObjectSpace ), -1, SPROP_COORD),
- SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 0 ), 11 ),
- SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 1 ), 11 ),
- SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 2 ), 11 ),
- SendPropInt( SENDINFO( m_EffectState ) ),
- SendPropBool( SENDINFO( m_bOpen ) ),
-#endif
-END_NETWORK_TABLE()
-
-#ifdef CLIENT_DLL
-BEGIN_PREDICTION_DATA( CWeaponPhysCannon )
- DEFINE_PRED_FIELD( m_EffectState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bOpen, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
-END_PREDICTION_DATA()
-#endif
-
-LINK_ENTITY_TO_CLASS( weapon_physcannon, CWeaponPhysCannon );
-PRECACHE_WEAPON_REGISTER( weapon_physcannon );
-
-#ifndef CLIENT_DLL
-
-acttable_t CWeaponPhysCannon::m_acttable[] =
-{
- { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false },
- { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false },
- { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false },
- { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false },
- { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false },
- { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false },
- { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponPhysCannon);
-
-#endif
-
-
-enum
-{
- ELEMENT_STATE_NONE = -1,
- ELEMENT_STATE_OPEN,
- ELEMENT_STATE_CLOSED,
-};
-
-enum
-{
- EFFECT_NONE,
- EFFECT_CLOSED,
- EFFECT_READY,
- EFFECT_HOLDING,
- EFFECT_LAUNCH,
-};
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CWeaponPhysCannon::CWeaponPhysCannon( void )
-{
- m_bOpen = false;
- m_nChangeState = ELEMENT_STATE_NONE;
- m_flCheckSuppressTime = 0.0f;
- m_EffectState = (int)EFFECT_NONE;
- m_flLastDenySoundPlayed = false;
-
-#ifdef CLIENT_DLL
- m_nOldEffectState = EFFECT_NONE;
- m_bOldOpen = false;
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Precache
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::Precache( void )
-{
- PrecacheModel( PHYSCANNON_BEAM_SPRITE );
- PrecacheModel( PHYSCANNON_BEAM_SPRITE_NOZ );
-
- PrecacheScriptSound( "Weapon_PhysCannon.HoldSound" );
-
- BaseClass::Precache();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Restore
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::OnRestore()
-{
- BaseClass::OnRestore();
- m_grabController.OnRestore();
-
- // Tracker 8106: Physcannon effects disappear through level transition, so
- // just recreate any effects here
- if ( m_EffectState != EFFECT_NONE )
- {
- DoEffect( m_EffectState, NULL );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// On Remove
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::UpdateOnRemove(void)
-{
- DestroyEffects( );
- BaseClass::UpdateOnRemove();
-}
-
-#ifdef CLIENT_DLL
-void CWeaponPhysCannon::OnDataChanged( DataUpdateType_t type )
-{
- BaseClass::OnDataChanged( type );
-
- if ( type == DATA_UPDATE_CREATED )
- {
- SetNextClientThink( CLIENT_THINK_ALWAYS );
-
- C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
- StartEffects();
- }
-
- if ( GetOwner() == NULL )
- {
- if ( m_hAttachedObject )
- {
- m_hAttachedObject->VPhysicsDestroyObject();
- }
-
- if ( m_hOldAttachedObject )
- {
- m_hOldAttachedObject->VPhysicsDestroyObject();
- }
- }
-
- // Update effect state when out of parity with the server
- if ( m_nOldEffectState != m_EffectState )
- {
- DoEffect( m_EffectState );
- m_nOldEffectState = m_EffectState;
- }
-
- // Update element state when out of parity
- if ( m_bOldOpen != m_bOpen )
- {
- if ( m_bOpen )
- {
- m_ElementParameter.InitFromCurrent( 1.0f, 0.2f, INTERP_SPLINE );
- }
- else
- {
- m_ElementParameter.InitFromCurrent( 0.0f, 0.5f, INTERP_SPLINE );
- }
-
- m_bOldOpen = (bool) m_bOpen;
- }
-}
-#endif
-
-//-----------------------------------------------------------------------------
-// Sprite scale factor
-//-----------------------------------------------------------------------------
-inline float CWeaponPhysCannon::SpriteScaleFactor()
-{
- return 1.0f;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponPhysCannon::Deploy( void )
-{
- CloseElements();
- DoEffect( EFFECT_READY );
-
- bool bReturn = BaseClass::Deploy();
-
- m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner )
- {
- pOwner->SetNextAttack( gpGlobals->curtime );
- }
-
- return bReturn;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::SetViewModel( void )
-{
- BaseClass::SetViewModel();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Force the cannon to drop anything it's carrying
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::ForceDrop( void )
-{
- CloseElements();
- DetachObject();
- StopEffects();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Drops its held entity if it matches the entity passed in
-// Input : *pTarget -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponPhysCannon::DropIfEntityHeld( CBaseEntity *pTarget )
-{
- if ( pTarget == NULL )
- return false;
-
- CBaseEntity *pHeld = m_grabController.GetAttached();
-
- if ( pHeld == NULL )
- return false;
-
- if ( pHeld == pTarget )
- {
- ForceDrop();
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::Drop( const Vector &vecVelocity )
-{
- ForceDrop();
-
-#ifndef CLIENT_DLL
- UTIL_Remove( this );
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CWeaponPhysCannon::CanHolster( void )
-{
- //Don't holster this weapon if we're holding onto something
- if ( m_bActive )
- return false;
-
- return BaseClass::CanHolster();
-};
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponPhysCannon::Holster( CBaseCombatWeapon *pSwitchingTo )
-{
- //Don't holster this weapon if we're holding onto something
- if ( m_bActive )
- return false;
-
- ForceDrop();
- DestroyEffects();
-
- return BaseClass::Holster( pSwitchingTo );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DryFire( void )
-{
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
-
- WeaponSound( EMPTY );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::PrimaryFireEffect( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- pOwner->ViewPunch( QAngle(-6, SharedRandomInt( "physcannonfire", -2,2) ,0) );
-
-#ifndef CLIENT_DLL
- color32 white = { 245, 245, 255, 32 };
- UTIL_ScreenFade( pOwner, white, 0.1f, 0.0f, FFADE_IN );
-#endif
-
- WeaponSound( SINGLE );
-}
-
-#define MAX_KNOCKBACK_FORCE 128
-
-//-----------------------------------------------------------------------------
-// Punt non-physics
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr )
-{
- if ( m_hLastPuntedObject == pEntity && gpGlobals->curtime < m_flRepuntObjectTime )
- return;
-
-#ifndef CLIENT_DLL
- CTakeDamageInfo info;
-
- info.SetAttacker( GetOwner() );
- info.SetInflictor( this );
- info.SetDamage( 1.0f );
- info.SetDamageType( DMG_CRUSH | DMG_PHYSGUN );
- info.SetDamageForce( forward ); // Scale?
- info.SetDamagePosition( tr.endpos );
-
- m_hLastPuntedObject = pEntity;
- m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
-
- pEntity->DispatchTraceAttack( info, forward, &tr );
-
- ApplyMultiDamage();
-
- //Explosion effect
- DoEffect( EFFECT_LAUNCH, &tr.endpos );
-#endif
-
- PrimaryFireEffect();
- SendWeaponAnim( ACT_VM_SECONDARYATTACK );
-
- m_nChangeState = ELEMENT_STATE_CLOSED;
- m_flElementDebounce = gpGlobals->curtime + 0.5f;
- m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
-}
-
-
-#ifndef CLIENT_DLL
-//-----------------------------------------------------------------------------
-// What happens when the physgun picks up something
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason )
-{
- // If the target is debris, convert it to non-debris
- if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
- {
- // Interactive debris converts back to debris when it comes to rest
- pEntity->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
- }
-
- Pickup_OnPhysGunPickup( pEntity, pOwner, reason );
-}
-#endif
-
-//-----------------------------------------------------------------------------
-// Punt vphysics
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::PuntVPhysics( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
-
- if ( m_hLastPuntedObject == pEntity && gpGlobals->curtime < m_flRepuntObjectTime )
- return;
-
- m_hLastPuntedObject = pEntity;
- m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
-
-#ifndef CLIENT_DLL
- CTakeDamageInfo info;
-
- Vector forward = vecForward;
-
- info.SetAttacker( GetOwner() );
- info.SetInflictor( this );
- info.SetDamage( 0.0f );
- info.SetDamageType( DMG_PHYSGUN );
- pEntity->DispatchTraceAttack( info, forward, &tr );
- ApplyMultiDamage();
-
-
- if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) )
- {
- IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
- int listCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
- if ( !listCount )
- {
- //FIXME: Do we want to do this if there's no physics object?
- Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
- DryFire();
- return;
- }
-
- if( forward.z < 0 )
- {
- //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets
- forward.z *= -0.65f;
- }
-
- // NOTE: Do this first to enable motion (if disabled) - so forces will work
- // Tell the object it's been punted
- Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
-
- // don't push vehicles that are attached to the world via fixed constraints
- // they will just wiggle...
- if ( (pList[0]->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC) && pEntity->GetServerVehicle() )
- {
- forward.Init();
- }
-
- if ( !Pickup_ShouldPuntUseLaunchForces( pEntity, PHYSGUN_FORCE_PUNTED ) )
- {
- int i;
-
- // limit mass to avoid punting REALLY huge things
- float totalMass = 0;
- for ( i = 0; i < listCount; i++ )
- {
- totalMass += pList[i]->GetMass();
- }
- float maxMass = 250;
- IServerVehicle *pVehicle = pEntity->GetServerVehicle();
- if ( pVehicle )
- {
- maxMass *= 2.5; // 625 for vehicles
- }
- float mass = MIN(totalMass, maxMass); // max 250kg of additional force
-
- // Put some spin on the object
- for ( i = 0; i < listCount; i++ )
- {
- const float hitObjectFactor = 0.5f;
- const float otherObjectFactor = 1.0f - hitObjectFactor;
- // Must be light enough
- float ratio = pList[i]->GetMass() / totalMass;
- if ( pList[i] == pEntity->VPhysicsGetObject() )
- {
- ratio += hitObjectFactor;
- ratio = MIN(ratio,1.0f);
- }
- else
- {
- ratio *= otherObjectFactor;
- }
- pList[i]->ApplyForceCenter( forward * 15000.0f * ratio );
- pList[i]->ApplyForceOffset( forward * mass * 600.0f * ratio, tr.endpos );
- }
- }
- else
- {
- ApplyVelocityBasedForce( pEntity, vecForward );
- }
- }
-
-#endif
- // Add recoil
- QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 );
- pOwner->ViewPunch( recoil );
-
- //Explosion effect
- DoEffect( EFFECT_LAUNCH, &tr.endpos );
-
- PrimaryFireEffect();
- SendWeaponAnim( ACT_VM_SECONDARYATTACK );
-
- m_nChangeState = ELEMENT_STATE_CLOSED;
- m_flElementDebounce = gpGlobals->curtime + 0.5f;
- m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
-
- // Don't allow the gun to regrab a thrown object!!
- m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Applies velocity-based forces to throw the entity. This code is
-// called from both punt and launch carried code.
-// ASSUMES: that pEntity is a vphysics entity.
-// Input : -
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward )
-{
-#ifndef CLIENT_DLL
- IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
- Assert(pPhysicsObject); // Shouldn't ever get here with a non-vphysics object.
- if (!pPhysicsObject)
- return;
-
- float flForceMax = physcannon_maxforce.GetFloat();
- float flForce = flForceMax;
-
- float mass = pPhysicsObject->GetMass();
- if (mass > 100)
- {
- mass = MIN(mass, 1000);
- float flForceMin = physcannon_minforce.GetFloat();
- flForce = SimpleSplineRemapVal(mass, 100, 600, flForceMax, flForceMin);
- }
-
- Vector vVel = forward * flForce;
- // FIXME: Josh needs to put a real value in for PHYSGUN_FORCE_PUNTED
- AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity, PHYSGUN_FORCE_PUNTED );
-
- pPhysicsObject->AddVelocity( &vVel, &aVel );
-
-#endif
-
-}
-
-
-//-----------------------------------------------------------------------------
-// Trace length
-//-----------------------------------------------------------------------------
-float CWeaponPhysCannon::TraceLength()
-{
- return physcannon_tracelength.GetFloat();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-// This mode is a toggle. Primary fire one time to pick up a physics object.
-// With an object held, click primary fire again to drop object.
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::PrimaryAttack( void )
-{
- if( m_flNextPrimaryAttack > gpGlobals->curtime )
- return;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- if( m_bActive )
- {
- // Punch the object being held!!
- Vector forward;
- pOwner->EyeVectors( &forward );
-
- // Validate the item is within punt range
- CBaseEntity *pHeld = m_grabController.GetAttached();
- Assert( pHeld != NULL );
-
- if ( pHeld != NULL )
- {
- float heldDist = ( pHeld->WorldSpaceCenter() - pOwner->WorldSpaceCenter() ).Length();
-
- if ( heldDist > physcannon_tracelength.GetFloat() )
- {
- // We can't punt this yet
- DryFire();
- return;
- }
- }
-
- LaunchObject( forward, physcannon_maxforce.GetFloat() );
-
- PrimaryFireEffect();
- SendWeaponAnim( ACT_VM_SECONDARYATTACK );
- return;
- }
-
- // If not active, just issue a physics punch in the world.
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
-
- Vector forward;
- pOwner->EyeVectors( &forward );
-
- // NOTE: Notice we're *not* using the mega tracelength here
- // when you have the mega cannon. Punting has shorter range.
- Vector start, end;
- start = pOwner->Weapon_ShootPosition();
- float flPuntDistance = physcannon_tracelength.GetFloat();
- VectorMA( start, flPuntDistance, forward, end );
-
- CTraceFilterNoOwnerTest filter( pOwner, COLLISION_GROUP_NONE );
- trace_t tr;
- UTIL_TraceHull( start, end, -Vector(8,8,8), Vector(8,8,8), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
- bool bValid = true;
- CBaseEntity *pEntity = tr.m_pEnt;
- if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
- {
- bValid = false;
- }
- else if ( (pEntity->GetMoveType() != MOVETYPE_VPHYSICS) && ( pEntity->m_takedamage == DAMAGE_NO ) )
- {
- bValid = false;
- }
-
- // If the entity we've hit is invalid, try a traceline instead
- if ( !bValid )
- {
- UTIL_TraceLine( start, end, MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
- if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
- {
- // Play dry-fire sequence
- DryFire();
- return;
- }
-
- pEntity = tr.m_pEnt;
- }
-
- // See if we hit something
- if ( pEntity->GetMoveType() != MOVETYPE_VPHYSICS )
- {
- if ( pEntity->m_takedamage == DAMAGE_NO )
- {
- DryFire();
- return;
- }
-
- if( GetOwner()->IsPlayer() )
- {
- // Don't let the player zap any NPC's except regular antlions and headcrabs.
- if( pEntity->IsPlayer() )
- {
- DryFire();
- return;
- }
- }
-
- PuntNonVPhysics( pEntity, forward, tr );
- }
- else
- {
- if ( pEntity->VPhysicsIsFlesh( ) )
- {
- DryFire();
- return;
- }
- PuntVPhysics( pEntity, forward, tr );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Click secondary attack whilst holding an object to hurl it.
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::SecondaryAttack( void )
-{
-#ifndef CLIENT_DLL
- if ( m_flNextSecondaryAttack > gpGlobals->curtime )
- return;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- // See if we should drop a held item
- if ( ( m_bActive ) && ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
- {
- // Drop the held object
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
- m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
-
- DetachObject();
-
- DoEffect( EFFECT_READY );
-
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
- }
- else
- {
- // Otherwise pick it up
- FindObjectResult_t result = FindObject();
- switch ( result )
- {
- case OBJECT_FOUND:
- WeaponSound( SPECIAL1 );
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
- m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
-
- // We found an object. Debounce the button
- m_nAttack2Debounce |= pOwner->m_nButtons;
- break;
-
- case OBJECT_NOT_FOUND:
- m_flNextSecondaryAttack = gpGlobals->curtime + 0.1f;
- CloseElements();
- break;
-
- case OBJECT_BEING_DETACHED:
- m_flNextSecondaryAttack = gpGlobals->curtime + 0.01f;
- break;
- }
-
- DoEffect( EFFECT_HOLDING );
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::WeaponIdle( void )
-{
- if ( HasWeaponIdleTimeElapsed() )
- {
- if ( m_bActive )
- {
- //Shake when holding an item
- SendWeaponAnim( ACT_VM_RELOAD );
- }
- else
- {
- //Otherwise idle simply
- SendWeaponAnim( ACT_VM_IDLE );
- }
- }
-}
-
-#ifndef CLIENT_DLL
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pObject -
-//-----------------------------------------------------------------------------
-bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosition )
-{
-
- if ( m_bActive )
- return false;
-
- if ( CanPickupObject( pObject ) == false )
- return false;
-
- m_grabController.SetIgnorePitch( false );
- m_grabController.SetAngleAlignment( 0 );
-
- IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
-
- // Must be valid
- if ( !pPhysics )
- return false;
-
- CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( GetOwner() );
-
- m_bActive = true;
- if( pOwner )
- {
- // NOTE: This can change the mass; so it must be done before max speed setting
- Physgun_OnPhysGunPickup( pObject, pOwner, PICKED_UP_BY_CANNON );
- }
-
- // NOTE :This must happen after OnPhysGunPickup because that can change the mass
- m_grabController.AttachEntity( pOwner, pObject, pPhysics, false, vPosition, false );
- m_hAttachedObject = pObject;
- m_attachedPositionObjectSpace = m_grabController.m_attachedPositionObjectSpace;
- m_attachedAnglesPlayerSpace = m_grabController.m_attachedAnglesPlayerSpace;
-
- m_bResetOwnerEntity = false;
-
- if ( m_hAttachedObject->GetOwnerEntity() == NULL )
- {
- m_hAttachedObject->SetOwnerEntity( pOwner );
- m_bResetOwnerEntity = true;
- }
-
-/* if( pOwner )
- {
- pOwner->EnableSprint( false );
-
- float loadWeight = ( 1.0f - GetLoadPercentage() );
- float maxSpeed = hl2_walkspeed.GetFloat() + ( ( hl2_normspeed.GetFloat() - hl2_walkspeed.GetFloat() ) * loadWeight );
-
- //Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() );
- pOwner->SetMaxSpeed( maxSpeed );
- }*/
-
- // Don't drop again for a slight delay, in case they were pulling objects near them
- m_flNextSecondaryAttack = gpGlobals->curtime + 0.4f;
-
- DoEffect( EFFECT_HOLDING );
- OpenElements();
-
- if ( GetMotorSound() )
- {
- (CSoundEnvelopeController::GetController()).Play( GetMotorSound(), 0.0f, 50 );
- (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 100, 0.5f );
- (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.8f, 0.5f );
- }
-
-
-
- return true;
-}
-
-CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void )
-{
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- Assert( pPlayer );
- if ( pPlayer == NULL )
- return OBJECT_NOT_FOUND;
-
- Vector forward;
- pPlayer->EyeVectors( &forward );
-
- // Setup our positions
- Vector start = pPlayer->Weapon_ShootPosition();
- float testLength = TraceLength() * 4.0f;
- Vector end = start + forward * testLength;
-
- // Try to find an object by looking straight ahead
- trace_t tr;
- CTraceFilterNoOwnerTest filter( pPlayer, COLLISION_GROUP_NONE );
- UTIL_TraceLine( start, end, MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
-
- // Try again with a hull trace
- if ( ( tr.fraction == 1.0 ) || ( tr.m_pEnt == NULL ) || ( tr.m_pEnt->IsWorld() ) )
- {
- UTIL_TraceHull( start, end, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
- }
-
- CBaseEntity *pEntity = tr.m_pEnt ? tr.m_pEnt->GetRootMoveParent() : NULL;
- bool bAttach = false;
- bool bPull = false;
-
- // If we hit something, pick it up or pull it
- if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt ) && ( tr.m_pEnt->IsWorld() == false ) )
- {
- // Attempt to attach if within range
- if ( tr.fraction <= 0.25f )
- {
- bAttach = true;
- }
- else if ( tr.fraction > 0.25f )
- {
- bPull = true;
- }
- }
-
- // Find anything within a general cone in front
- CBaseEntity *pConeEntity = NULL;
-
- if (!bAttach && !bPull)
- {
- pConeEntity = FindObjectInCone( start, forward, physcannon_cone.GetFloat() );
- }
-
- if ( pConeEntity )
- {
- pEntity = pConeEntity;
-
- // If the object is near, grab it. Else, pull it a bit.
- if ( pEntity->WorldSpaceCenter().DistToSqr( start ) <= (testLength * testLength) )
- {
- bAttach = true;
- }
- else
- {
- bPull = true;
- }
- }
-
- if ( CanPickupObject( pEntity ) == false )
- {
- // Make a noise to signify we can't pick this up
- if ( !m_flLastDenySoundPlayed )
- {
- m_flLastDenySoundPlayed = true;
- WeaponSound( SPECIAL3 );
- }
-
- return OBJECT_NOT_FOUND;
- }
-
- // Check to see if the object is constrained + needs to be ripped off...
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- if ( !Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PICKED_UP_BY_CANNON ) )
- return OBJECT_BEING_DETACHED;
-
- if ( bAttach )
- {
- return AttachObject( pEntity, tr.endpos ) ? OBJECT_FOUND : OBJECT_NOT_FOUND;
- }
-
- if ( !bPull )
- return OBJECT_NOT_FOUND;
-
- // FIXME: This needs to be run through the CanPickupObject logic
- IPhysicsObject *pObj = pEntity->VPhysicsGetObject();
- if ( !pObj )
- return OBJECT_NOT_FOUND;
-
- // If we're too far, simply start to pull the object towards us
- Vector pullDir = start - pEntity->WorldSpaceCenter();
- VectorNormalize( pullDir );
- pullDir *= physcannon_pullforce.GetFloat();
-
- float mass = PhysGetEntityMass( pEntity );
- if ( mass < 50.0f )
- {
- pullDir *= (mass + 0.5) * (1/50.0f);
- }
-
- // Nudge it towards us
- pObj->ApplyForceCenter( pullDir );
- return OBJECT_NOT_FOUND;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-CBaseEntity *CWeaponPhysCannon::FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone )
-{
- // Find the nearest physics-based item in a cone in front of me.
- CBaseEntity *list[256];
- float flNearestDist = TraceLength() + 1.0;
- Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist );
- Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist );
-
- CBaseEntity *pNearest = NULL;
-
- int count = UTIL_EntitiesInBox( list, 256, mins, maxs, 0 );
- for( int i = 0 ; i < count ; i++ )
- {
- if ( !list[ i ]->VPhysicsGetObject() )
- continue;
-
- // Closer than other objects
- Vector los = ( list[ i ]->WorldSpaceCenter() - vecOrigin );
- float flDist = VectorNormalize( los );
- if( flDist >= flNearestDist )
- continue;
-
- // Cull to the cone
- if ( DotProduct( los, vecDir ) <= flCone )
- continue;
-
- // Make sure it isn't occluded!
- trace_t tr;
- CTraceFilterNoOwnerTest filter( GetOwner(), COLLISION_GROUP_NONE );
- UTIL_TraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
- if( tr.m_pEnt == list[ i ] )
- {
- flNearestDist = flDist;
- pNearest = list[ i ];
- }
- }
-
- return pNearest;
-}
-
-#endif
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError )
-{
- CBaseEntity *pEntity = GetAttached();
- if ( !pEntity )
- return false;
- if ( ComputeError() > flError )
- return false;
- if ( pPlayer->GetGroundEntity() == pEntity )
- return false;
- if (!pEntity->VPhysicsGetObject() )
- return false;
-
- //Adrian: Oops, our object became motion disabled, let go!
- IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
- if ( pPhys && pPhys->IsMoveable() == false )
- {
- return false;
- }
-
- if ( m_frameCount == gpGlobals->framecount )
- {
- return true;
- }
- m_frameCount = gpGlobals->framecount;
- Vector forward, right, up;
- QAngle playerAngles = pPlayer->EyeAngles();
-
- float pitch = AngleDistance(playerAngles.x,0);
- playerAngles.x = clamp( pitch, -75, 75 );
- AngleVectors( playerAngles, &forward, &right, &up );
-
- // Now clamp a sphere of object radius at end to the player's bbox
- Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -forward );
- Vector player2d = pPlayer->CollisionProp()->OBBMaxs();
- float playerRadius = player2d.Length2D();
- float flDot = DotProduct( forward, radial );
-
- float radius = playerRadius + fabs( flDot );
-
- float distance = 24 + ( radius * 2.0f );
-
- Vector start = pPlayer->Weapon_ShootPosition();
- Vector end = start + ( forward * distance );
-
- trace_t tr;
- CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE );
- Ray_t ray;
- ray.Init( start, end );
- enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
-
- if ( tr.fraction < 0.5 )
- {
- end = start + forward * (radius*0.5f);
- }
- else if ( tr.fraction <= 1.0f )
- {
- end = start + forward * ( distance - radius );
- }
-
- Vector playerMins, playerMaxs, nearest;
- pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs );
- Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter();
- CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL );
-
- Vector delta = end - nearest;
- float len = VectorNormalize(delta);
- if ( len < radius )
- {
- end = nearest + radius * delta;
- }
-
- QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer );
-
- //Show overlays of radius
- if ( g_debug_physcannon.GetBool() )
- {
-
-#ifdef CLIENT_DLL
-
- debugoverlay->AddBoxOverlay( end, -Vector( 2,2,2 ), Vector(2,2,2), angles, 0, 255, 255, true, 0 );
-
- debugoverlay->AddBoxOverlay( GetAttached()->WorldSpaceCenter(),
- -Vector( radius, radius, radius),
- Vector( radius, radius, radius ),
- angles,
- 255, 255, 0,
- true,
- 0.0f );
-
-#else
-
- NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 );
-
- NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(),
- -Vector( radius+5, radius+5, radius+5),
- Vector( radius+5, radius+5, radius+5 ),
- 255, 0, 0,
- true,
- 0.0f );
-#endif
- }
-
-#ifndef CLIENT_DLL
- // If it has a preferred orientation, update to ensure we're still oriented correctly.
- Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
-
-
- // We may be holding a prop that has preferred carry angles
- if ( m_bHasPreferredCarryAngles )
- {
- matrix3x4_t tmp;
- ComputePlayerMatrix( pPlayer, tmp );
- angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp );
- }
-
-#endif
-
- matrix3x4_t attachedToWorld;
- Vector offset;
- AngleMatrix( angles, attachedToWorld );
- VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset );
-
- SetTargetPosition( end - offset, angles );
-
- return true;
-}
-
-void CWeaponPhysCannon::UpdateObject( void )
-{
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
- Assert( pPlayer );
-
- float flError = 12;
- if ( !m_grabController.UpdateObject( pPlayer, flError ) )
- {
- DetachObject();
- return;
- }
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched )
-{
-#ifndef CLIENT_DLL
- if ( m_bActive == false )
- return;
-
- CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( GetOwner() );
- if( pOwner != NULL )
- {
- pOwner->EnableSprint( true );
- pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() );
- }
-
- CBaseEntity *pObject = m_grabController.GetAttached();
-
- m_grabController.DetachEntity( wasLaunched );
-
- if ( pObject != NULL )
- {
- Pickup_OnPhysGunDrop( pObject, pOwner, wasLaunched ? LAUNCHED_BY_CANNON : DROPPED_BY_CANNON );
- }
-
- // Stop our looping sound
- if ( GetMotorSound() )
- {
- (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
- (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
- }
-
- if ( pObject && m_bResetOwnerEntity == true )
- {
- pObject->SetOwnerEntity( NULL );
- }
-
- m_bActive = false;
- m_hAttachedObject = NULL;
-
-
- if ( playSound )
- {
- //Play the detach sound
- WeaponSound( MELEE_MISS );
- }
-
-#else
-
- m_grabController.DetachEntity( wasLaunched );
-
- if ( m_hAttachedObject )
- {
- m_hAttachedObject->VPhysicsDestroyObject();
- }
-#endif
-}
-
-
-#ifdef CLIENT_DLL
-void CWeaponPhysCannon::ManagePredictedObject( void )
-{
- CBaseEntity *pAttachedObject = m_hAttachedObject.Get();
-
- if ( m_hAttachedObject )
- {
- // NOTE :This must happen after OnPhysGunPickup because that can change the mass
- if ( pAttachedObject != GetGrabController().GetAttached() )
- {
- IPhysicsObject *pPhysics = pAttachedObject->VPhysicsGetObject();
-
- if ( pPhysics == NULL )
- {
- solid_t tmpSolid;
- PhysModelParseSolid( tmpSolid, m_hAttachedObject, pAttachedObject->GetModelIndex() );
-
- pAttachedObject->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false, &tmpSolid );
- }
-
- pPhysics = pAttachedObject->VPhysicsGetObject();
-
- if ( pPhysics )
- {
- m_grabController.SetIgnorePitch( false );
- m_grabController.SetAngleAlignment( 0 );
-
- GetGrabController().AttachEntity( ToBasePlayer( GetOwner() ), pAttachedObject, pPhysics, false, vec3_origin, false );
- GetGrabController().m_attachedPositionObjectSpace = m_attachedPositionObjectSpace;
- GetGrabController().m_attachedAnglesPlayerSpace = m_attachedAnglesPlayerSpace;
- }
- }
- }
- else
- {
- if ( m_hOldAttachedObject && m_hOldAttachedObject->VPhysicsGetObject() )
- {
- GetGrabController().DetachEntity( false );
-
- m_hOldAttachedObject->VPhysicsDestroyObject();
- }
- }
-
- m_hOldAttachedObject = m_hAttachedObject;
-}
-
-#endif
-
-#ifdef CLIENT_DLL
-
-//-----------------------------------------------------------------------------
-// Purpose: Update the pose parameter for the gun
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::UpdateElementPosition( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- float flElementPosition = m_ElementParameter.Interp( gpGlobals->curtime );
-
- if ( ShouldDrawUsingViewModel() )
- {
- if ( pOwner != NULL )
- {
- CBaseViewModel *vm = pOwner->GetViewModel();
-
- if ( vm != NULL )
- {
- vm->SetPoseParameter( "active", flElementPosition );
- }
- }
- }
- else
- {
- SetPoseParameter( "active", flElementPosition );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Think function for the client
-//-----------------------------------------------------------------------------
-
-void CWeaponPhysCannon::ClientThink( void )
-{
- // Update our elements visually
- UpdateElementPosition();
-
- // Update our effects
- DoEffectIdle();
-}
-
-#endif // CLIENT_DLL
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::ItemPreFrame()
-{
- BaseClass::ItemPreFrame();
-
-#ifdef CLIENT_DLL
- C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();
-
- if ( localplayer && !localplayer->IsObserver() )
- ManagePredictedObject();
-#endif
-
- // Update the object if the weapon is switched on.
- if( m_bActive )
- {
- UpdateObject();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::CheckForTarget( void )
-{
-#ifndef CLIENT_DLL
- //See if we're suppressing this
- if ( m_flCheckSuppressTime > gpGlobals->curtime )
- return;
-
- // holstered
- if ( IsEffectActive( EF_NODRAW ) )
- return;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- if ( m_bActive )
- return;
-
- Vector aimDir;
- pOwner->EyeVectors( &aimDir );
-
- Vector startPos = pOwner->Weapon_ShootPosition();
- Vector endPos;
- VectorMA( startPos, TraceLength(), aimDir, endPos );
-
- trace_t tr;
- UTIL_TraceHull( startPos, endPos, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT|CONTENTS_GRATE, pOwner, COLLISION_GROUP_NONE, &tr );
-
- if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt != NULL ) )
- {
- // FIXME: Try just having the elements always open when pointed at a physics object
- if ( CanPickupObject( tr.m_pEnt ) || Pickup_ForcePhysGunOpen( tr.m_pEnt, pOwner ) )
- // if ( ( tr.m_pEnt->VPhysicsGetObject() != NULL ) && ( tr.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS ) )
- {
- m_nChangeState = ELEMENT_STATE_NONE;
- OpenElements();
- return;
- }
- }
-
- // Close the elements after a delay to prevent overact state switching
- if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState == ELEMENT_STATE_NONE ) )
- {
- m_nChangeState = ELEMENT_STATE_CLOSED;
- m_flElementDebounce = gpGlobals->curtime + 0.5f;
- }
-#endif
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Idle effect (pulsing)
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DoEffectIdle( void )
-{
-#ifdef CLIENT_DLL
-
- StartEffects();
-
- //if ( ShouldDrawUsingViewModel() )
- {
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
- {
- m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 0.075f, 0.05f ) * SPRITE_SCALE );
- m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 24, 32 ) );
- }
-
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
- {
- m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 3, 5 ) );
- m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 200, 255 ) );
- }
-
- if ( m_EffectState != EFFECT_HOLDING )
- {
- // Turn beams off
- m_Beams[0].SetVisible( false );
- m_Beams[1].SetVisible( false );
- m_Beams[2].SetVisible( false );
- }
- }
- /*
- else
- {
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
- {
- m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 0.075f, 0.05f ) * SPRITE_SCALE );
- m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 24, 32 ) );
- }
-
- // Turn on the glow sprites
- for ( i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
- {
- m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 3, 5 ) );
- m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 200, 255 ) );
- }
-
- if ( m_EffectState != EFFECT_HOLDING )
- {
- // Turn beams off
- m_Beams[0].SetVisible( false );
- m_Beams[1].SetVisible( false );
- m_Beams[2].SetVisible( false );
- }
- }
- */
-#endif
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::ItemPostFrame()
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- if ( pOwner == NULL )
- {
- // We found an object. Debounce the button
- m_nAttack2Debounce = 0;
- return;
- }
-
- //Check for object in pickup range
- if ( m_bActive == false )
- {
- CheckForTarget();
-
- if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState != ELEMENT_STATE_NONE ) )
- {
- if ( m_nChangeState == ELEMENT_STATE_OPEN )
- {
- OpenElements();
- }
- else if ( m_nChangeState == ELEMENT_STATE_CLOSED )
- {
- CloseElements();
- }
-
- m_nChangeState = ELEMENT_STATE_NONE;
- }
- }
-
- // NOTE: Attack2 will be considered to be pressed until the first item is picked up.
- int nAttack2Mask = pOwner->m_nButtons & (~m_nAttack2Debounce);
- if ( nAttack2Mask & IN_ATTACK2 )
- {
- SecondaryAttack();
- }
- else
- {
- // Reset our debouncer
- m_flLastDenySoundPlayed = false;
-
- if ( m_bActive == false )
- {
- DoEffect( EFFECT_READY );
- }
- }
-
- if (( pOwner->m_nButtons & IN_ATTACK2 ) == 0 )
- {
- m_nAttack2Debounce = 0;
- }
-
- if ( pOwner->m_nButtons & IN_ATTACK )
- {
- PrimaryAttack();
- }
- else
- {
- WeaponIdle();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-#define PHYSCANNON_DANGER_SOUND_RADIUS 128
-
-void CWeaponPhysCannon::LaunchObject( const Vector &vecDir, float flForce )
-{
- CBaseEntity *pObject = m_grabController.GetAttached();
-
- if ( !(m_hLastPuntedObject == pObject && gpGlobals->curtime < m_flRepuntObjectTime) )
- {
- // FIRE!!!
- if( pObject != NULL )
- {
- DetachObject( false, true );
-
- m_hLastPuntedObject = pObject;
- m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
-
- // Launch
- ApplyVelocityBasedForce( pObject, vecDir );
-
- // Don't allow the gun to regrab a thrown object!!
- m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
-
- Vector center = pObject->WorldSpaceCenter();
-
- //Do repulse effect
- DoEffect( EFFECT_LAUNCH, &center );
-
- m_hAttachedObject = NULL;
- m_bActive = false;
- }
- }
-
- // Stop our looping sound
- if ( GetMotorSound() )
- {
- (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
- (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
- }
-
- //Close the elements and suppress checking for a bit
- m_nChangeState = ELEMENT_STATE_CLOSED;
- m_flElementDebounce = gpGlobals->curtime + 0.1f;
- m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
-}
-
-bool UTIL_IsCombineBall( CBaseEntity *pEntity );
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pTarget -
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponPhysCannon::CanPickupObject( CBaseEntity *pTarget )
-{
-#ifndef CLIENT_DLL
- if ( pTarget == NULL )
- return false;
-
- if ( pTarget->GetBaseAnimating() && pTarget->GetBaseAnimating()->IsDissolving() )
- return false;
-
- if ( pTarget->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
- return false;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner && pOwner->GetGroundEntity() == pTarget )
- return false;
-
- if ( pTarget->VPhysicsIsFlesh( ) )
- return false;
-
- IPhysicsObject *pObj = pTarget->VPhysicsGetObject();
-
- if ( pObj && pObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
- return false;
-
- if ( UTIL_IsCombineBall( pTarget ) )
- {
- return CBasePlayer::CanPickupObject( pTarget, 0, 0 );
- }
-
- return CBasePlayer::CanPickupObject( pTarget, physcannon_maxmass.GetFloat(), 0 );
-#else
- return false;
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::OpenElements( void )
-{
- if ( m_bOpen )
- return;
-
- WeaponSound( SPECIAL2 );
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- SendWeaponAnim( ACT_VM_IDLE );
-
- m_bOpen = true;
-
- DoEffect( EFFECT_READY );
-
-#ifdef CLIENT
- // Element prediction
- m_ElementParameter.InitFromCurrent( 1.0f, 0.2f, INTERP_SPLINE );
- m_bOldOpen = true;
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::CloseElements( void )
-{
- if ( m_bOpen == false )
- return;
-
- WeaponSound( MELEE_HIT );
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- SendWeaponAnim( ACT_VM_IDLE );
-
- m_bOpen = false;
-
- if ( GetMotorSound() )
- {
- (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
- (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
- }
-
- DoEffect( EFFECT_CLOSED );
-
-#ifdef CLIENT
- // Element prediction
- m_ElementParameter.InitFromCurrent( 0.0f, 0.5f, INTERP_SPLINE );
- m_bOldOpen = false;
-#endif
-}
-
-#define PHYSCANNON_MAX_MASS 500
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : float
-//-----------------------------------------------------------------------------
-float CWeaponPhysCannon::GetLoadPercentage( void )
-{
- float loadWeight = m_grabController.GetLoadWeight();
- loadWeight /= physcannon_maxmass.GetFloat();
- loadWeight = clamp( loadWeight, 0.0f, 1.0f );
- return loadWeight;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : CSoundPatch
-//-----------------------------------------------------------------------------
-CSoundPatch *CWeaponPhysCannon::GetMotorSound( void )
-{
- if ( m_sndMotor == NULL )
- {
- CPASAttenuationFilter filter( this );
-
- m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_PhysCannon.HoldSound", ATTN_NORM );
- }
-
- return m_sndMotor;
-}
-
-
-//-----------------------------------------------------------------------------
-// Shuts down sounds
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::StopLoopingSounds()
-{
- if ( m_sndMotor != NULL )
- {
- (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndMotor );
- m_sndMotor = NULL;
- }
-
-#ifndef CLIENT_DLL
- BaseClass::StopLoopingSounds();
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DestroyEffects( void )
-{
-#ifdef CLIENT_DLL
-
- // Free our beams
- m_Beams[0].Release();
- m_Beams[1].Release();
- m_Beams[2].Release();
-
-#endif
-
- // Stop everything
- StopEffects();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::StopEffects( bool stopSound )
-{
- // Turn off our effect state
- DoEffect( EFFECT_NONE );
-
-#ifndef CLIENT_DLL
- //Shut off sounds
- if ( stopSound && GetMotorSound() != NULL )
- {
- (CSoundEnvelopeController::GetController()).SoundFadeOut( GetMotorSound(), 0.1f );
- }
-#endif // !CLIENT_DLL
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::StartEffects( void )
-{
-#ifdef CLIENT_DLL
-
- // ------------------------------------------
- // Core
- // ------------------------------------------
-
- if ( m_Parameters[PHYSCANNON_CORE].GetMaterial() == NULL )
- {
- m_Parameters[PHYSCANNON_CORE].GetScale().Init( 0.0f, 1.0f, 0.1f );
- m_Parameters[PHYSCANNON_CORE].GetAlpha().Init( 255.0f, 255.0f, 0.1f );
- m_Parameters[PHYSCANNON_CORE].SetAttachment( 1 );
-
- if ( m_Parameters[PHYSCANNON_CORE].SetMaterial( PHYSCANNON_CENTER_GLOW ) == false )
- {
- // This means the texture was not found
- Assert( 0 );
- }
- }
-
- // ------------------------------------------
- // Blast
- // ------------------------------------------
-
- if ( m_Parameters[PHYSCANNON_BLAST].GetMaterial() == NULL )
- {
- m_Parameters[PHYSCANNON_BLAST].GetScale().Init( 0.0f, 1.0f, 0.1f );
- m_Parameters[PHYSCANNON_BLAST].GetAlpha().Init( 255.0f, 255.0f, 0.1f );
- m_Parameters[PHYSCANNON_BLAST].SetAttachment( 1 );
- m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
-
- if ( m_Parameters[PHYSCANNON_BLAST].SetMaterial( PHYSCANNON_BLAST_SPRITE ) == false )
- {
- // This means the texture was not found
- Assert( 0 );
- }
- }
-
- // ------------------------------------------
- // Glows
- // ------------------------------------------
-
- const char *attachNamesGlowThirdPerson[NUM_GLOW_SPRITES] =
- {
- "fork1m",
- "fork1t",
- "fork2m",
- "fork2t",
- "fork3m",
- "fork3t",
- };
-
- const char *attachNamesGlow[NUM_GLOW_SPRITES] =
- {
- "fork1b",
- "fork1m",
- "fork1t",
- "fork2b",
- "fork2m",
- "fork2t"
- };
-
- //Create the glow sprites
- for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
- {
- if ( m_Parameters[i].GetMaterial() != NULL )
- continue;
-
- m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
- m_Parameters[i].GetAlpha().SetAbsolute( 64.0f );
-
- // Different for different views
- if ( ShouldDrawUsingViewModel() )
- {
- m_Parameters[i].SetAttachment( LookupAttachment( attachNamesGlow[i-PHYSCANNON_GLOW1] ) );
- }
- else
- {
- m_Parameters[i].SetAttachment( LookupAttachment( attachNamesGlowThirdPerson[i-PHYSCANNON_GLOW1] ) );
- }
- m_Parameters[i].SetColor( Vector( 255, 128, 0 ) );
-
- if ( m_Parameters[i].SetMaterial( PHYSCANNON_GLOW_SPRITE ) == false )
- {
- // This means the texture was not found
- Assert( 0 );
- }
- }
-
- // ------------------------------------------
- // End caps
- // ------------------------------------------
-
- const char *attachNamesEndCap[NUM_ENDCAP_SPRITES] =
- {
- "fork1t",
- "fork2t",
- "fork3t"
- };
-
- //Create the glow sprites
- for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
- {
- if ( m_Parameters[i].GetMaterial() != NULL )
- continue;
-
- m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
- m_Parameters[i].GetAlpha().SetAbsolute( 255.0f );
- m_Parameters[i].SetAttachment( LookupAttachment( attachNamesEndCap[i-PHYSCANNON_ENDCAP1] ) );
- m_Parameters[i].SetVisible( false );
-
- if ( m_Parameters[i].SetMaterial( PHYSCANNON_ENDCAP_SPRITE ) == false )
- {
- // This means the texture was not found
- Assert( 0 );
- }
- }
-
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Closing effects
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DoEffectClosed( void )
-{
-
-#ifdef CLIENT_DLL
-
- // Turn off the end-caps
- for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
- {
- m_Parameters[i].SetVisible( false );
- }
-
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Ready effects
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DoEffectReady( void )
-{
-
-#ifdef CLIENT_DLL
-
- // Special POV case
- if ( ShouldDrawUsingViewModel() )
- {
- //Turn on the center sprite
- m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 14.0f, 0.2f );
- m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 128.0f, 0.2f );
- m_Parameters[PHYSCANNON_CORE].SetVisible();
- }
- else
- {
- //Turn off the center sprite
- m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 8.0f, 0.2f );
- m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 0.0f, 0.2f );
- m_Parameters[PHYSCANNON_CORE].SetVisible();
- }
-
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
- {
- m_Parameters[i].GetScale().InitFromCurrent( 0.4f * SPRITE_SCALE, 0.2f );
- m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
- m_Parameters[i].SetVisible();
- }
-
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
- {
- m_Parameters[i].SetVisible( false );
- }
-
-#endif
-
-}
-
-
-//-----------------------------------------------------------------------------
-// Holding effects
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DoEffectHolding( void )
-{
-
-#ifdef CLIENT_DLL
-
- if ( ShouldDrawUsingViewModel() )
- {
- // Scale up the center sprite
- m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 16.0f, 0.2f );
- m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 255.0f, 0.1f );
- m_Parameters[PHYSCANNON_CORE].SetVisible();
-
- // Prepare for scale up
- m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
-
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
- {
- m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
- m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
- m_Parameters[i].SetVisible();
- }
-
- // Turn on the glow sprites
- // NOTE: The last glow is left off for first-person
- for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES-1); i++ )
- {
- m_Parameters[i].SetVisible();
- }
-
- // Create our beams
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- CBaseEntity *pBeamEnt = pOwner->GetViewModel();
-
- // Setup the beams
- m_Beams[0].Init( LookupAttachment( "fork1t" ), 1, pBeamEnt, true );
- m_Beams[1].Init( LookupAttachment( "fork2t" ), 1, pBeamEnt, true );
-
- // Set them visible
- m_Beams[0].SetVisible();
- m_Beams[1].SetVisible();
- }
- else
- {
- // Scale up the center sprite
- m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 14.0f, 0.2f );
- m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 255.0f, 0.1f );
- m_Parameters[PHYSCANNON_CORE].SetVisible();
-
- // Prepare for scale up
- m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
-
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
- {
- m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
- m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
- m_Parameters[i].SetVisible();
- }
-
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
- {
- m_Parameters[i].SetVisible();
- }
-
- // Setup the beams
- m_Beams[0].Init( LookupAttachment( "fork1t" ), 1, this, false );
- m_Beams[1].Init( LookupAttachment( "fork2t" ), 1, this, false );
- m_Beams[2].Init( LookupAttachment( "fork3t" ), 1, this, false );
-
- // Set them visible
- m_Beams[0].SetVisible();
- m_Beams[1].SetVisible();
- m_Beams[2].SetVisible();
- }
-
-#endif
-
-}
-
-
-//-----------------------------------------------------------------------------
-// Launch effects
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DoEffectLaunch( Vector *pos )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- if ( pOwner == NULL )
- return;
-
- Vector endPos;
- Vector shotDir;
-
- // See if we need to predict this position
- if ( pos == NULL )
- {
- // Hit an entity if we're holding one
- if ( m_hAttachedObject )
- {
- endPos = m_hAttachedObject->WorldSpaceCenter();
-
- shotDir = endPos - pOwner->Weapon_ShootPosition();
- VectorNormalize( shotDir );
- }
- else
- {
- // Otherwise try and find the right spot
- endPos = pOwner->Weapon_ShootPosition();
- pOwner->EyeVectors( &shotDir );
-
- trace_t tr;
- UTIL_TraceLine( endPos, endPos + ( shotDir * MAX_TRACE_LENGTH ), MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
-
- endPos = tr.endpos;
- shotDir = endPos - pOwner->Weapon_ShootPosition();
- VectorNormalize( shotDir );
- }
- }
- else
- {
- // Use what is supplied
- endPos = *pos;
- shotDir = ( endPos - pOwner->Weapon_ShootPosition() );
- VectorNormalize( shotDir );
- }
-
- // End hit
- CPVSFilter filter( endPos );
-
- // Don't send this to the owning player, they already had it predicted
- if ( IsPredicted() )
- {
- filter.UsePredictionRules();
- }
-
- // Do an impact hit
- CEffectData data;
- data.m_vOrigin = endPos;
-#ifdef CLIENT_DLL
- data.m_hEntity = GetRefEHandle();
-#else
- data.m_nEntIndex = entindex();
-#endif
-
- te->DispatchEffect( filter, 0.0, data.m_vOrigin, "PhyscannonImpact", data );
-
-#ifdef CLIENT_DLL
-
- //Turn on the blast sprite and scale
- m_Parameters[PHYSCANNON_BLAST].GetScale().Init( 8.0f, 64.0f, 0.1f );
- m_Parameters[PHYSCANNON_BLAST].GetAlpha().Init( 255.0f, 0.0f, 0.2f );
- m_Parameters[PHYSCANNON_BLAST].SetVisible();
-
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Shutdown for the weapon when it's holstered
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DoEffectNone( void )
-{
-#ifdef CLIENT_DLL
-
- //Turn off main glows
- m_Parameters[PHYSCANNON_CORE].SetVisible( false );
- m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
-
- for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
- {
- m_Parameters[i].SetVisible( false );
- }
-
- // Turn on the glow sprites
- for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
- {
- m_Parameters[i].SetVisible( false );
- }
-
- m_Beams[0].SetVisible( false );
- m_Beams[1].SetVisible( false );
- m_Beams[2].SetVisible( false );
-
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : effectType -
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DoEffect( int effectType, Vector *pos )
-{
- m_EffectState = effectType;
-
-#ifdef CLIENT_DLL
- // Save predicted state
- m_nOldEffectState = m_EffectState;
-#endif
-
- switch( effectType )
- {
- case EFFECT_CLOSED:
- DoEffectClosed( );
- break;
-
- case EFFECT_READY:
- DoEffectReady( );
- break;
-
- case EFFECT_HOLDING:
- DoEffectHolding();
- break;
-
- case EFFECT_LAUNCH:
- DoEffectLaunch( pos );
- break;
-
- default:
- case EFFECT_NONE:
- DoEffectNone();
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : iIndex -
-// Output : const char
-//-----------------------------------------------------------------------------
-const char *CWeaponPhysCannon::GetShootSound( int iIndex ) const
-{
- return BaseClass::GetShootSound( iIndex );
-}
-
-#ifdef CLIENT_DLL
-
-extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
-
-//-----------------------------------------------------------------------------
-// Purpose: Gets the complete list of values needed to render an effect from an
-// effect parameter
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment )
-{
- const float dt = gpGlobals->curtime;
-
- // Get alpha
- float alpha = m_Parameters[effectID].GetAlpha().Interp( dt );
-
- // Get scale
- scale = m_Parameters[effectID].GetScale().Interp( dt );
-
- // Get material
- *pMaterial = (IMaterial *) m_Parameters[effectID].GetMaterial();
-
- // Setup the color
- color.r = (int) m_Parameters[effectID].GetColor().x;
- color.g = (int) m_Parameters[effectID].GetColor().y;
- color.b = (int) m_Parameters[effectID].GetColor().z;
- color.a = (int) alpha;
-
- // Setup the attachment
- int attachment = m_Parameters[effectID].GetAttachment();
- QAngle angles;
-
- // Format for first-person
- if ( ShouldDrawUsingViewModel() )
- {
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner != NULL )
- {
- pOwner->GetViewModel()->GetAttachment( attachment, vecAttachment, angles );
- ::FormatViewModelAttachment( vecAttachment, true );
- }
- }
- else
- {
- GetAttachment( attachment, vecAttachment, angles );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Whether or not an effect is set to display
-//-----------------------------------------------------------------------------
-bool CWeaponPhysCannon::IsEffectVisible( EffectType_t effectID )
-{
- return m_Parameters[effectID].IsVisible();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draws the effect sprite, given an effect parameter ID
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DrawEffectSprite( EffectType_t effectID )
-{
- color32 color;
- float scale;
- IMaterial *pMaterial;
- Vector vecAttachment;
-
- // Don't draw invisible effects
- if ( IsEffectVisible( effectID ) == false )
- return;
-
- // Get all of our parameters
- GetEffectParameters( effectID, color, scale, &pMaterial, vecAttachment );
-
- // Msg( "Scale: %.2f\tAlpha: %.2f\n", scale, alpha );
-
- // Don't render fully translucent objects
- if ( color.a <= 0.0f )
- return;
-
- // Draw the sprite
- CMatRenderContextPtr pRenderContext( materials );
- pRenderContext->Bind( pMaterial, this );
- DrawSprite( vecAttachment, scale, scale, color );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Render our third-person effects
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::DrawEffects( void )
-{
- // Draw the core effects
- DrawEffectSprite( PHYSCANNON_CORE );
- DrawEffectSprite( PHYSCANNON_BLAST );
-
- // Draw the glows
- for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
- {
- DrawEffectSprite( (EffectType_t) i );
- }
-
- // Draw the endcaps
- for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
- {
- DrawEffectSprite( (EffectType_t) i );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Third-person function call to render world model
-//-----------------------------------------------------------------------------
-int CWeaponPhysCannon::DrawModel( int flags )
-{
- // Only render these on the transparent pass
- if ( flags & STUDIO_TRANSPARENCY )
- {
- DrawEffects();
- return 1;
- }
-
- // Only do this on the opaque pass
- return BaseClass::DrawModel( flags );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: First-person function call after viewmodel has been drawn
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
-{
- // Render our effects
- DrawEffects();
-
- // Pass this back up
- BaseClass::ViewModelDrawn( pBaseViewModel );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: We are always considered transparent
-//-----------------------------------------------------------------------------
-bool CWeaponPhysCannon::IsTransparent( void )
-{
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPhysCannon::NotifyShouldTransmit( ShouldTransmitState_t state )
-{
- BaseClass::NotifyShouldTransmit(state);
-
- if ( state == SHOULDTRANSMIT_END )
- {
- DoEffect( EFFECT_NONE );
- }
-}
-
-#endif
-
-//-----------------------------------------------------------------------------
-// EXTERNAL API
-//-----------------------------------------------------------------------------
-void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis )
-{
- CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
- if ( pCannon )
- {
- if ( pOnlyIfHoldingThis )
- {
- pCannon->DropIfEntityHeld( pOnlyIfHoldingThis );
- }
- else
- {
- pCannon->ForceDrop();
- }
- }
-}
-
-bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupControllerEntity, CBaseEntity *pHeldEntity )
-{
- CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
-
- return pController ? pController->IsHoldingEntity( pHeldEntity ) : false;
-}
-
-
-float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject )
-{
- float mass = 0.0f;
- CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
- if ( pCannon )
- {
- CGrabController &grab = pCannon->GetGrabController();
- mass = grab.GetSavedMass( pHeldObject );
- }
-
- return mass;
-}
-
-CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon )
-{
- CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
- if ( pCannon )
- {
- CGrabController &grab = pCannon->GetGrabController();
- return grab.GetAttached();
- }
-
- return NULL;
-}
-
-float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject )
-{
- float mass = 0.0f;
- CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
- if ( pController )
- {
- CGrabController &grab = pController->GetGrabController();
- mass = grab.GetSavedMass( pHeldObject );
- }
- return mass;
-}
-
-#ifdef CLIENT_DLL
-
-extern void FX_GaussExplosion( const Vector &pos, const Vector &dir, int type );
-
-void CallbackPhyscannonImpact( const CEffectData &data )
-{
- C_BaseEntity *pEnt = data.GetEntity();
- if ( pEnt == NULL )
- return;
-
- Vector vecAttachment;
- QAngle vecAngles;
-
- C_BaseCombatWeapon *pWeapon = dynamic_cast<C_BaseCombatWeapon *>(pEnt);
-
- if ( pWeapon == NULL )
- return;
-
- pWeapon->GetAttachment( 1, vecAttachment, vecAngles );
-
- Vector dir = ( data.m_vOrigin - vecAttachment );
- VectorNormalize( dir );
-
- // Do special first-person fix-up
- if ( pWeapon->GetOwner() == CBasePlayer::GetLocalPlayer() )
- {
- // Translate the attachment entity to the viewmodel
- C_BasePlayer *pPlayer = dynamic_cast<C_BasePlayer *>(pWeapon->GetOwner());
-
- if ( pPlayer )
- {
- pEnt = pPlayer->GetViewModel();
- }
-
- // Format attachment for first-person view!
- ::FormatViewModelAttachment( vecAttachment, true );
-
- // Explosions at the impact point
- FX_GaussExplosion( data.m_vOrigin, -dir, 0 );
-
- // Draw a beam
- BeamInfo_t beamInfo;
-
- beamInfo.m_pStartEnt = pEnt;
- beamInfo.m_nStartAttachment = 1;
- beamInfo.m_pEndEnt = NULL;
- beamInfo.m_nEndAttachment = -1;
- beamInfo.m_vecStart = vec3_origin;
- beamInfo.m_vecEnd = data.m_vOrigin;
- beamInfo.m_pszModelName = PHYSCANNON_BEAM_SPRITE;
- beamInfo.m_flHaloScale = 0.0f;
- beamInfo.m_flLife = 0.1f;
- beamInfo.m_flWidth = 12.0f;
- beamInfo.m_flEndWidth = 4.0f;
- beamInfo.m_flFadeLength = 0.0f;
- beamInfo.m_flAmplitude = 0;
- beamInfo.m_flBrightness = 255.0;
- beamInfo.m_flSpeed = 0.0f;
- beamInfo.m_nStartFrame = 0.0;
- beamInfo.m_flFrameRate = 30.0;
- beamInfo.m_flRed = 255.0;
- beamInfo.m_flGreen = 255.0;
- beamInfo.m_flBlue = 255.0;
- beamInfo.m_nSegments = 16;
- beamInfo.m_bRenderable = true;
- beamInfo.m_nFlags = FBEAM_ONLYNOISEONCE;
-
- beams->CreateBeamEntPoint( beamInfo );
- }
- else
- {
- // Explosion at the starting point
- FX_GaussExplosion( vecAttachment, dir, 0 );
- }
-}
-
-DECLARE_CLIENT_EFFECT( "PhyscannonImpact", CallbackPhyscannonImpact );
-
-#endif
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Physics cannon
+//
+//=============================================================================//
+
+#include "cbase.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+ #include "vcollide_parse.h"
+ #include "engine/ivdebugoverlay.h"
+ #include "iviewrender_beams.h"
+ #include "beamdraw.h"
+ #include "c_te_effect_dispatch.h"
+ #include "model_types.h"
+ #include "clienteffectprecachesystem.h"
+ #include "fx_interpvalue.h"
+#else
+ #include "hl2mp_player.h"
+ #include "soundent.h"
+ #include "ndebugoverlay.h"
+ #include "ai_basenpc.h"
+ #include "player_pickup.h"
+ #include "physics_prop_ragdoll.h"
+ #include "globalstate.h"
+ #include "props.h"
+ #include "te_effect_dispatch.h"
+ #include "util.h"
+#endif
+
+#include "gamerules.h"
+#include "soundenvelope.h"
+#include "engine/IEngineSound.h"
+#include "physics.h"
+#include "in_buttons.h"
+#include "IEffects.h"
+#include "shake.h"
+#include "beam_shared.h"
+#include "Sprite.h"
+#include "weapon_physcannon.h"
+#include "physics_saverestore.h"
+#include "movevars_shared.h"
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+#include "vphysics/friction.h"
+#include "debugoverlay_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define SPRITE_SCALE 128.0f
+
+static const char *s_pWaitForUpgradeContext = "WaitForUpgrade";
+
+ConVar g_debug_physcannon( "g_debug_physcannon", "0", FCVAR_REPLICATED | FCVAR_CHEAT );
+
+ConVar physcannon_minforce( "physcannon_minforce", "700", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_maxforce( "physcannon_maxforce", "1500", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_maxmass( "physcannon_maxmass", "250", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_tracelength( "physcannon_tracelength", "250", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_chargetime("physcannon_chargetime", "2", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_pullforce( "physcannon_pullforce", "4000", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_cone( "physcannon_cone", "0.97", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar physcannon_ball_cone( "physcannon_ball_cone", "0.997", FCVAR_REPLICATED | FCVAR_CHEAT );
+ConVar player_throwforce( "player_throwforce", "1000", FCVAR_REPLICATED | FCVAR_CHEAT );
+
+#ifndef CLIENT_DLL
+extern ConVar hl2_normspeed;
+extern ConVar hl2_walkspeed;
+#endif
+
+#define PHYSCANNON_BEAM_SPRITE "sprites/orangelight1.vmt"
+#define PHYSCANNON_BEAM_SPRITE_NOZ "sprites/orangelight1_noz.vmt"
+#define PHYSCANNON_GLOW_SPRITE "sprites/glow04_noz"
+#define PHYSCANNON_ENDCAP_SPRITE "sprites/orangeflare1"
+#define PHYSCANNON_CENTER_GLOW "sprites/orangecore1"
+#define PHYSCANNON_BLAST_SPRITE "sprites/orangecore2"
+
+#ifdef CLIENT_DLL
+
+ //Precahce the effects
+ CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectPhysCannon )
+ CLIENTEFFECT_MATERIAL( "sprites/orangelight1" )
+ CLIENTEFFECT_MATERIAL( "sprites/orangelight1_noz" )
+ CLIENTEFFECT_MATERIAL( PHYSCANNON_GLOW_SPRITE )
+ CLIENTEFFECT_MATERIAL( PHYSCANNON_ENDCAP_SPRITE )
+ CLIENTEFFECT_MATERIAL( PHYSCANNON_CENTER_GLOW )
+ CLIENTEFFECT_MATERIAL( PHYSCANNON_BLAST_SPRITE )
+ CLIENTEFFECT_REGISTER_END()
+
+#endif // CLIENT_DLL
+
+#ifndef CLIENT_DLL
+
+void PhysCannonBeginUpgrade( CBaseAnimating *pAnim )
+{
+
+}
+
+bool PlayerHasMegaPhysCannon( void )
+{
+ return false;
+}
+
+bool PhysCannonAccountableForObject( CBaseCombatWeapon *pPhysCannon, CBaseEntity *pObject )
+{
+ // BRJ: FIXME! This can't be implemented trivially, so I'm leaving it to Steve or Adrian
+ Assert( 0 );
+ return false;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// this will hit skip the pass entity, but not anything it owns
+// (lets player grab own grenades)
+class CTraceFilterNoOwnerTest : public CTraceFilterSimple
+{
+public:
+ DECLARE_CLASS( CTraceFilterNoOwnerTest, CTraceFilterSimple );
+
+ CTraceFilterNoOwnerTest( const IHandleEntity *passentity, int collisionGroup )
+ : CTraceFilterSimple( NULL, collisionGroup ), m_pPassNotOwner(passentity)
+ {
+ }
+
+ virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
+ {
+ if ( pHandleEntity != m_pPassNotOwner )
+ return BaseClass::ShouldHitEntity( pHandleEntity, contentsMask );
+
+ return false;
+ }
+
+protected:
+ const IHandleEntity *m_pPassNotOwner;
+};
+
+static void MatrixOrthogonalize( matrix3x4_t &matrix, int column )
+{
+ Vector columns[3];
+ int i;
+
+ for ( i = 0; i < 3; i++ )
+ {
+ MatrixGetColumn( matrix, i, columns[i] );
+ }
+
+ int index0 = column;
+ int index1 = (column+1)%3;
+ int index2 = (column+2)%3;
+
+ columns[index2] = CrossProduct( columns[index0], columns[index1] );
+ columns[index1] = CrossProduct( columns[index2], columns[index0] );
+ VectorNormalize( columns[index2] );
+ VectorNormalize( columns[index1] );
+ MatrixSetColumn( columns[index1], index1, matrix );
+ MatrixSetColumn( columns[index2], index2, matrix );
+}
+
+#define SIGN(x) ( (x) < 0 ? -1 : 1 )
+
+static QAngle AlignAngles( const QAngle &angles, float cosineAlignAngle )
+{
+ matrix3x4_t alignMatrix;
+ AngleMatrix( angles, alignMatrix );
+
+ // NOTE: Must align z first
+ for ( int j = 3; --j >= 0; )
+ {
+ Vector vec;
+ MatrixGetColumn( alignMatrix, j, vec );
+ for ( int i = 0; i < 3; i++ )
+ {
+ if ( fabs(vec[i]) > cosineAlignAngle )
+ {
+ vec[i] = SIGN(vec[i]);
+ vec[(i+1)%3] = 0;
+ vec[(i+2)%3] = 0;
+ MatrixSetColumn( vec, j, alignMatrix );
+ MatrixOrthogonalize( alignMatrix, j );
+ break;
+ }
+ }
+ }
+
+ QAngle out;
+ MatrixAngles( alignMatrix, out );
+ return out;
+}
+
+
+static void TraceCollideAgainstBBox( const CPhysCollide *pCollide, const Vector &start, const Vector &end, const QAngle &angles, const Vector &boxOrigin, const Vector &mins, const Vector &maxs, trace_t *ptr )
+{
+ physcollision->TraceBox( boxOrigin, boxOrigin + (start-end), mins, maxs, pCollide, start, angles, ptr );
+
+ if ( ptr->DidHit() )
+ {
+ ptr->endpos = start * (1-ptr->fraction) + end * ptr->fraction;
+ ptr->startpos = start;
+ ptr->plane.dist = -ptr->plane.dist;
+ ptr->plane.normal *= -1;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Computes a local matrix for the player clamped to valid carry ranges
+//-----------------------------------------------------------------------------
+// when looking level, hold bottom of object 8 inches below eye level
+#define PLAYER_HOLD_LEVEL_EYES -8
+
+// when looking down, hold bottom of object 0 inches from feet
+#define PLAYER_HOLD_DOWN_FEET 2
+
+// when looking up, hold bottom of object 24 inches above eye level
+#define PLAYER_HOLD_UP_EYES 24
+
+// use a +/-30 degree range for the entire range of motion of pitch
+#define PLAYER_LOOK_PITCH_RANGE 30
+
+// player can reach down 2ft below his feet (otherwise he'll hold the object above the bottom)
+#define PLAYER_REACH_DOWN_DISTANCE 24
+
+static void ComputePlayerMatrix( CBasePlayer *pPlayer, matrix3x4_t &out )
+{
+ if ( !pPlayer )
+ return;
+
+ QAngle angles = pPlayer->EyeAngles();
+ Vector origin = pPlayer->EyePosition();
+
+ // 0-360 / -180-180
+ //angles.x = init ? 0 : AngleDistance( angles.x, 0 );
+ //angles.x = clamp( angles.x, -PLAYER_LOOK_PITCH_RANGE, PLAYER_LOOK_PITCH_RANGE );
+ angles.x = 0;
+
+ float feet = pPlayer->GetAbsOrigin().z + pPlayer->WorldAlignMins().z;
+ float eyes = origin.z;
+ float zoffset = 0;
+ // moving up (negative pitch is up)
+ if ( angles.x < 0 )
+ {
+ zoffset = RemapVal( angles.x, 0, -PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_UP_EYES );
+ }
+ else
+ {
+ zoffset = RemapVal( angles.x, 0, PLAYER_LOOK_PITCH_RANGE, PLAYER_HOLD_LEVEL_EYES, PLAYER_HOLD_DOWN_FEET + (feet - eyes) );
+ }
+ origin.z += zoffset;
+ angles.x = 0;
+ AngleMatrix( angles, origin, out );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+
+// derive from this so we can add save/load data to it
+struct game_shadowcontrol_params_t : public hlshadowcontrol_params_t
+{
+ DECLARE_SIMPLE_DATADESC();
+};
+
+BEGIN_SIMPLE_DATADESC( game_shadowcontrol_params_t )
+
+ DEFINE_FIELD( targetPosition, FIELD_POSITION_VECTOR ),
+ DEFINE_FIELD( targetRotation, FIELD_VECTOR ),
+ DEFINE_FIELD( maxAngular, FIELD_FLOAT ),
+ DEFINE_FIELD( maxDampAngular, FIELD_FLOAT ),
+ DEFINE_FIELD( maxSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( maxDampSpeed, FIELD_FLOAT ),
+ DEFINE_FIELD( dampFactor, FIELD_FLOAT ),
+ DEFINE_FIELD( teleportDistance, FIELD_FLOAT ),
+
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+class CGrabController : public IMotionEvent
+{
+public:
+
+ CGrabController( void );
+ ~CGrabController( void );
+ void AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition );
+ void DetachEntity( bool bClearVelocity );
+ void OnRestore();
+
+ bool UpdateObject( CBasePlayer *pPlayer, float flError );
+
+ void SetTargetPosition( const Vector &target, const QAngle &targetOrientation );
+ float ComputeError();
+ float GetLoadWeight( void ) const { return m_flLoadWeight; }
+ void SetAngleAlignment( float alignAngleCosine ) { m_angleAlignment = alignAngleCosine; }
+ void SetIgnorePitch( bool bIgnore ) { m_bIgnoreRelativePitch = bIgnore; }
+ QAngle TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
+ QAngle TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer );
+
+ CBaseEntity *GetAttached() { return (CBaseEntity *)m_attachedEntity; }
+
+ IMotionEvent::simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
+ float GetSavedMass( IPhysicsObject *pObject );
+
+ QAngle m_attachedAnglesPlayerSpace;
+ Vector m_attachedPositionObjectSpace;
+
+private:
+ // Compute the max speed for an attached object
+ void ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics );
+
+ game_shadowcontrol_params_t m_shadow;
+ float m_timeToArrive;
+ float m_errorTime;
+ float m_error;
+ float m_contactAmount;
+ float m_angleAlignment;
+ bool m_bCarriedEntityBlocksLOS;
+ bool m_bIgnoreRelativePitch;
+
+ float m_flLoadWeight;
+ float m_savedRotDamping[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ float m_savedMass[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ EHANDLE m_attachedEntity;
+ QAngle m_vecPreferredCarryAngles;
+ bool m_bHasPreferredCarryAngles;
+
+
+ IPhysicsMotionController *m_controller;
+ int m_frameCount;
+ friend class CWeaponPhysCannon;
+};
+
+const float DEFAULT_MAX_ANGULAR = 360.0f * 10.0f;
+const float REDUCED_CARRY_MASS = 1.0f;
+
+CGrabController::CGrabController( void )
+{
+ m_shadow.dampFactor = 1.0;
+ m_shadow.teleportDistance = 0;
+ m_errorTime = 0;
+ m_error = 0;
+ // make this controller really stiff!
+ m_shadow.maxSpeed = 1000;
+ m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
+ m_shadow.maxDampSpeed = m_shadow.maxSpeed*2;
+ m_shadow.maxDampAngular = m_shadow.maxAngular;
+ m_attachedEntity = NULL;
+ m_vecPreferredCarryAngles = vec3_angle;
+ m_bHasPreferredCarryAngles = false;
+}
+
+CGrabController::~CGrabController( void )
+{
+ DetachEntity( false );
+}
+
+void CGrabController::OnRestore()
+{
+ if ( m_controller )
+ {
+ m_controller->SetEventHandler( this );
+ }
+}
+
+void CGrabController::SetTargetPosition( const Vector &target, const QAngle &targetOrientation )
+{
+ m_shadow.targetPosition = target;
+ m_shadow.targetRotation = targetOrientation;
+
+ m_timeToArrive = gpGlobals->frametime;
+
+ CBaseEntity *pAttached = GetAttached();
+ if ( pAttached )
+ {
+ IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
+
+ if ( pObj != NULL )
+ {
+ pObj->Wake();
+ }
+ else
+ {
+ DetachEntity( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CGrabController::ComputeError()
+{
+ if ( m_errorTime <= 0 )
+ return 0;
+
+ CBaseEntity *pAttached = GetAttached();
+ if ( pAttached )
+ {
+ Vector pos;
+ IPhysicsObject *pObj = pAttached->VPhysicsGetObject();
+
+ if ( pObj )
+ {
+ pObj->GetShadowPosition( &pos, NULL );
+
+ float error = (m_shadow.targetPosition - pos).Length();
+ if ( m_errorTime > 0 )
+ {
+ if ( m_errorTime > 1 )
+ {
+ m_errorTime = 1;
+ }
+ float speed = error / m_errorTime;
+ if ( speed > m_shadow.maxSpeed )
+ {
+ error *= 0.5;
+ }
+ m_error = (1-m_errorTime) * m_error + error * m_errorTime;
+ }
+ }
+ else
+ {
+ DevMsg( "Object attached to Physcannon has no physics object\n" );
+ DetachEntity( false );
+ return 9999; // force detach
+ }
+ }
+
+ if ( pAttached->IsEFlagSet( EFL_IS_BEING_LIFTED_BY_BARNACLE ) )
+ {
+ m_error *= 3.0f;
+ }
+
+ m_errorTime = 0;
+
+ return m_error;
+}
+
+
+#define MASS_SPEED_SCALE 60
+#define MAX_MASS 40
+
+void CGrabController::ComputeMaxSpeed( CBaseEntity *pEntity, IPhysicsObject *pPhysics )
+{
+#ifndef CLIENT_DLL
+ m_shadow.maxSpeed = 1000;
+ m_shadow.maxAngular = DEFAULT_MAX_ANGULAR;
+
+ // Compute total mass...
+ float flMass = PhysGetEntityMass( pEntity );
+ float flMaxMass = physcannon_maxmass.GetFloat();
+ if ( flMass <= flMaxMass )
+ return;
+
+ float flLerpFactor = clamp( flMass, flMaxMass, 500.0f );
+ flLerpFactor = SimpleSplineRemapVal( flLerpFactor, flMaxMass, 500.0f, 0.0f, 1.0f );
+
+ float invMass = pPhysics->GetInvMass();
+ float invInertia = pPhysics->GetInvInertia().Length();
+
+ float invMaxMass = 1.0f / MAX_MASS;
+ float ratio = invMaxMass / invMass;
+ invMass = invMaxMass;
+ invInertia *= ratio;
+
+ float maxSpeed = invMass * MASS_SPEED_SCALE * 200;
+ float maxAngular = invInertia * MASS_SPEED_SCALE * 360;
+
+ m_shadow.maxSpeed = Lerp( flLerpFactor, m_shadow.maxSpeed, maxSpeed );
+ m_shadow.maxAngular = Lerp( flLerpFactor, m_shadow.maxAngular, maxAngular );
+#endif
+}
+
+
+QAngle CGrabController::TransformAnglesToPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
+{
+ if ( m_bIgnoreRelativePitch )
+ {
+ matrix3x4_t test;
+ QAngle angleTest = pPlayer->EyeAngles();
+ angleTest.x = 0;
+ AngleMatrix( angleTest, test );
+ return TransformAnglesToLocalSpace( anglesIn, test );
+ }
+ return TransformAnglesToLocalSpace( anglesIn, pPlayer->EntityToWorldTransform() );
+}
+
+QAngle CGrabController::TransformAnglesFromPlayerSpace( const QAngle &anglesIn, CBasePlayer *pPlayer )
+{
+ if ( m_bIgnoreRelativePitch )
+ {
+ matrix3x4_t test;
+ QAngle angleTest = pPlayer->EyeAngles();
+ angleTest.x = 0;
+ AngleMatrix( angleTest, test );
+ return TransformAnglesToWorldSpace( anglesIn, test );
+ }
+ return TransformAnglesToWorldSpace( anglesIn, pPlayer->EntityToWorldTransform() );
+}
+
+
+void CGrabController::AttachEntity( CBasePlayer *pPlayer, CBaseEntity *pEntity, IPhysicsObject *pPhys, bool bIsMegaPhysCannon, const Vector &vGrabPosition, bool bUseGrabPosition )
+{
+ // play the impact sound of the object hitting the player
+ // used as feedback to let the player know he picked up the object
+#ifndef CLIENT_DLL
+ PhysicsImpactSound( pPlayer, pPhys, CHAN_STATIC, pPhys->GetMaterialIndex(), pPlayer->VPhysicsGetObject()->GetMaterialIndex(), 1.0, 64 );
+#endif
+ Vector position;
+ QAngle angles;
+ pPhys->GetPosition( &position, &angles );
+ // If it has a preferred orientation, use that instead.
+#ifndef CLIENT_DLL
+ Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
+#endif
+
+// ComputeMaxSpeed( pEntity, pPhys );
+
+ // Carried entities can never block LOS
+ m_bCarriedEntityBlocksLOS = pEntity->BlocksLOS();
+ pEntity->SetBlocksLOS( false );
+ m_controller = physenv->CreateMotionController( this );
+ m_controller->AttachObject( pPhys, true );
+ // Don't do this, it's causing trouble with constraint solvers.
+ //m_controller->SetPriority( IPhysicsMotionController::HIGH_PRIORITY );
+
+ pPhys->Wake();
+ PhysSetGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
+ SetTargetPosition( position, angles );
+ m_attachedEntity = pEntity;
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+ m_flLoadWeight = 0;
+ float damping = 10;
+ float flFactor = count / 7.5f;
+ if ( flFactor < 1.0f )
+ {
+ flFactor = 1.0f;
+ }
+ for ( int i = 0; i < count; i++ )
+ {
+ float mass = pList[i]->GetMass();
+ pList[i]->GetDamping( NULL, &m_savedRotDamping[i] );
+ m_flLoadWeight += mass;
+ m_savedMass[i] = mass;
+
+ // reduce the mass to prevent the player from adding crazy amounts of energy to the system
+ pList[i]->SetMass( REDUCED_CARRY_MASS / flFactor );
+ pList[i]->SetDamping( NULL, &damping );
+ }
+
+ // Give extra mass to the phys object we're actually picking up
+ pPhys->SetMass( REDUCED_CARRY_MASS );
+ pPhys->EnableDrag( false );
+
+ m_errorTime = -1.0f; // 1 seconds until error starts accumulating
+ m_error = 0;
+ m_contactAmount = 0;
+
+ m_attachedAnglesPlayerSpace = TransformAnglesToPlayerSpace( angles, pPlayer );
+ if ( m_angleAlignment != 0 )
+ {
+ m_attachedAnglesPlayerSpace = AlignAngles( m_attachedAnglesPlayerSpace, m_angleAlignment );
+ }
+
+ VectorITransform( pEntity->WorldSpaceCenter(), pEntity->EntityToWorldTransform(), m_attachedPositionObjectSpace );
+
+#ifndef CLIENT_DLL
+ // If it's a prop, see if it has desired carry angles
+ CPhysicsProp *pProp = dynamic_cast<CPhysicsProp *>(pEntity);
+ if ( pProp )
+ {
+ m_bHasPreferredCarryAngles = pProp->GetPropDataAngles( "preferred_carryangles", m_vecPreferredCarryAngles );
+ }
+ else
+ {
+ m_bHasPreferredCarryAngles = false;
+ }
+#else
+
+ m_bHasPreferredCarryAngles = false;
+#endif
+
+}
+
+static void ClampPhysicsVelocity( IPhysicsObject *pPhys, float linearLimit, float angularLimit )
+{
+ Vector vel;
+ AngularImpulse angVel;
+ pPhys->GetVelocity( &vel, &angVel );
+ float speed = VectorNormalize(vel) - linearLimit;
+ float angSpeed = VectorNormalize(angVel) - angularLimit;
+ speed = speed < 0 ? 0 : -speed;
+ angSpeed = angSpeed < 0 ? 0 : -angSpeed;
+ vel *= speed;
+ angVel *= angSpeed;
+ pPhys->AddVelocity( &vel, &angVel );
+}
+
+void CGrabController::DetachEntity( bool bClearVelocity )
+{
+ CBaseEntity *pEntity = GetAttached();
+ if ( pEntity )
+ {
+ // Restore the LS blocking state
+ pEntity->SetBlocksLOS( m_bCarriedEntityBlocksLOS );
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int count = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+
+ for ( int i = 0; i < count; i++ )
+ {
+ IPhysicsObject *pPhys = pList[i];
+ if ( !pPhys )
+ continue;
+
+ // on the odd chance that it's gone to sleep while under anti-gravity
+ pPhys->EnableDrag( true );
+ pPhys->Wake();
+ pPhys->SetMass( m_savedMass[i] );
+ pPhys->SetDamping( NULL, &m_savedRotDamping[i] );
+ PhysClearGameFlags( pPhys, FVPHYSICS_PLAYER_HELD );
+ if ( bClearVelocity )
+ {
+ PhysForceClearVelocity( pPhys );
+ }
+ else
+ {
+#ifndef CLIENT_DLL
+ ClampPhysicsVelocity( pPhys, hl2_normspeed.GetFloat() * 1.5f, 2.0f * 360.0f );
+#endif
+ }
+
+ }
+ }
+
+ m_attachedEntity = NULL;
+ if ( physenv )
+ {
+ physenv->DestroyMotionController( m_controller );
+ }
+ m_controller = NULL;
+}
+
+static bool InContactWithHeavyObject( IPhysicsObject *pObject, float heavyMass )
+{
+ bool contact = false;
+ IPhysicsFrictionSnapshot *pSnapshot = pObject->CreateFrictionSnapshot();
+ while ( pSnapshot->IsValid() )
+ {
+ IPhysicsObject *pOther = pSnapshot->GetObject( 1 );
+ if ( !pOther->IsMoveable() || pOther->GetMass() > heavyMass )
+ {
+ contact = true;
+ break;
+ }
+ pSnapshot->NextFrictionData();
+ }
+ pObject->DestroyFrictionSnapshot( pSnapshot );
+ return contact;
+}
+
+IMotionEvent::simresult_e CGrabController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
+{
+ game_shadowcontrol_params_t shadowParams = m_shadow;
+ if ( InContactWithHeavyObject( pObject, GetLoadWeight() ) )
+ {
+ m_contactAmount = Approach( 0.1f, m_contactAmount, deltaTime*2.0f );
+ }
+ else
+ {
+ m_contactAmount = Approach( 1.0f, m_contactAmount, deltaTime*2.0f );
+ }
+ shadowParams.maxAngular = m_shadow.maxAngular * m_contactAmount * m_contactAmount * m_contactAmount;
+#ifndef CLIENT_DLL
+ m_timeToArrive = pObject->ComputeShadowControl( shadowParams, m_timeToArrive, deltaTime );
+#else
+ m_timeToArrive = pObject->ComputeShadowControl( shadowParams, (TICK_INTERVAL*2), deltaTime );
+#endif
+
+ // Slide along the current contact points to fix bouncing problems
+ Vector velocity;
+ AngularImpulse angVel;
+ pObject->GetVelocity( &velocity, &angVel );
+ PhysComputeSlideDirection( pObject, velocity, angVel, &velocity, &angVel, GetLoadWeight() );
+ pObject->SetVelocityInstantaneous( &velocity, NULL );
+
+ linear.Init();
+ angular.Init();
+ m_errorTime += deltaTime;
+
+ return SIM_LOCAL_ACCELERATION;
+}
+
+float CGrabController::GetSavedMass( IPhysicsObject *pObject )
+{
+ CBaseEntity *pHeld = m_attachedEntity;
+ if ( pHeld )
+ {
+ if ( pObject->GetGameData() == (void*)pHeld )
+ {
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int count = pHeld->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+ for ( int i = 0; i < count; i++ )
+ {
+ if ( pList[i] == pObject )
+ return m_savedMass[i];
+ }
+ }
+ }
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Player pickup controller
+//-----------------------------------------------------------------------------
+
+class CPlayerPickupController : public CBaseEntity
+{
+ DECLARE_CLASS( CPlayerPickupController, CBaseEntity );
+public:
+ void Init( CBasePlayer *pPlayer, CBaseEntity *pObject );
+ void Shutdown( bool bThrown = false );
+ bool OnControls( CBaseEntity *pControls ) { return true; }
+ void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ void OnRestore()
+ {
+ m_grabController.OnRestore();
+ }
+ void VPhysicsUpdate( IPhysicsObject *pPhysics ){}
+ void VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) {}
+
+ bool IsHoldingEntity( CBaseEntity *pEnt );
+ CGrabController &GetGrabController() { return m_grabController; }
+
+private:
+ CGrabController m_grabController;
+ CBasePlayer *m_pPlayer;
+};
+
+LINK_ENTITY_TO_CLASS( player_pickup, CPlayerPickupController );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+// *pObject -
+//-----------------------------------------------------------------------------
+void CPlayerPickupController::Init( CBasePlayer *pPlayer, CBaseEntity *pObject )
+{
+#ifndef CLIENT_DLL
+ // Holster player's weapon
+ if ( pPlayer->GetActiveWeapon() )
+ {
+ if ( !pPlayer->GetActiveWeapon()->Holster() )
+ {
+ Shutdown();
+ return;
+ }
+ }
+
+
+ CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( pPlayer );
+ if ( pOwner )
+ {
+ pOwner->EnableSprint( false );
+ }
+
+ // If the target is debris, convert it to non-debris
+ if ( pObject->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
+ {
+ // Interactive debris converts back to debris when it comes to rest
+ pObject->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
+ }
+
+ // done so I'll go across level transitions with the player
+ SetParent( pPlayer );
+ m_grabController.SetIgnorePitch( true );
+ m_grabController.SetAngleAlignment( DOT_30DEGREE );
+ m_pPlayer = pPlayer;
+ IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
+ Pickup_OnPhysGunPickup( pObject, m_pPlayer );
+
+ m_grabController.AttachEntity( pPlayer, pObject, pPhysics, false, vec3_origin, false );
+
+ m_pPlayer->m_Local.m_iHideHUD |= HIDEHUD_WEAPONSELECTION;
+ m_pPlayer->SetUseEntity( this );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bool -
+//-----------------------------------------------------------------------------
+void CPlayerPickupController::Shutdown( bool bThrown )
+{
+#ifndef CLIENT_DLL
+ CBaseEntity *pObject = m_grabController.GetAttached();
+
+ bool bClearVelocity = false;
+ if ( !bThrown && pObject && pObject->VPhysicsGetObject() && pObject->VPhysicsGetObject()->GetContactPoint(NULL,NULL) )
+ {
+ bClearVelocity = true;
+ }
+
+ m_grabController.DetachEntity( bClearVelocity );
+
+ if ( pObject != NULL )
+ {
+ Pickup_OnPhysGunDrop( pObject, m_pPlayer, bThrown ? THROWN_BY_PLAYER : DROPPED_BY_PLAYER );
+ }
+
+ if ( m_pPlayer )
+ {
+ CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( m_pPlayer );
+ if ( pOwner )
+ {
+ pOwner->EnableSprint( true );
+ }
+
+ m_pPlayer->SetUseEntity( NULL );
+ if ( m_pPlayer->GetActiveWeapon() )
+ {
+ if ( !m_pPlayer->GetActiveWeapon()->Deploy() )
+ {
+ // We tried to restore the player's weapon, but we couldn't.
+ // This usually happens when they're holding an empty weapon that doesn't
+ // autoswitch away when out of ammo. Switch to next best weapon.
+ m_pPlayer->SwitchToNextBestWeapon( NULL );
+ }
+ }
+
+ m_pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION;
+ }
+ Remove();
+
+#endif
+
+}
+
+
+void CPlayerPickupController::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ if ( ToBasePlayer(pActivator) == m_pPlayer )
+ {
+ CBaseEntity *pAttached = m_grabController.GetAttached();
+
+ // UNDONE: Use vphysics stress to decide to drop objects
+ // UNDONE: Must fix case of forcing objects into the ground you're standing on (causes stress) before that will work
+ if ( !pAttached || useType == USE_OFF || (m_pPlayer->m_nButtons & IN_ATTACK2) || m_grabController.ComputeError() > 12 )
+ {
+ Shutdown();
+ return;
+ }
+
+ //Adrian: Oops, our object became motion disabled, let go!
+ IPhysicsObject *pPhys = pAttached->VPhysicsGetObject();
+ if ( pPhys && pPhys->IsMoveable() == false )
+ {
+ Shutdown();
+ return;
+ }
+
+#if STRESS_TEST
+ vphysics_objectstress_t stress;
+ CalculateObjectStress( pPhys, pAttached, &stress );
+ if ( stress.exertedStress > 250 )
+ {
+ Shutdown();
+ return;
+ }
+#endif
+ // +ATTACK will throw phys objects
+ if ( m_pPlayer->m_nButtons & IN_ATTACK )
+ {
+ Shutdown( true );
+ Vector vecLaunch;
+ m_pPlayer->EyeVectors( &vecLaunch );
+ // JAY: Scale this with mass because some small objects really go flying
+ float massFactor = clamp( pPhys->GetMass(), 0.5, 15 );
+ massFactor = RemapVal( massFactor, 0.5, 15, 0.5, 4 );
+ vecLaunch *= player_throwforce.GetFloat() * massFactor;
+
+ pPhys->ApplyForceCenter( vecLaunch );
+ AngularImpulse aVel = RandomAngularImpulse( -10, 10 ) * massFactor;
+ pPhys->ApplyTorqueCenter( aVel );
+ return;
+ }
+
+ if ( useType == USE_SET )
+ {
+ // update position
+ m_grabController.UpdateObject( m_pPlayer, 12 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pEnt -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CPlayerPickupController::IsHoldingEntity( CBaseEntity *pEnt )
+{
+ return ( m_grabController.GetAttached() == pEnt );
+}
+
+void PlayerPickupObject( CBasePlayer *pPlayer, CBaseEntity *pObject )
+{
+
+#ifndef CLIENT_DLL
+
+ //Don't pick up if we don't have a phys object.
+ if ( pObject->VPhysicsGetObject() == NULL )
+ return;
+
+ CPlayerPickupController *pController = (CPlayerPickupController *)CBaseEntity::Create( "player_pickup", pObject->GetAbsOrigin(), vec3_angle, pPlayer );
+
+ if ( !pController )
+ return;
+
+ pController->Init( pPlayer, pObject );
+
+#endif
+
+}
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CInterpolatedValue class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+#ifdef CLIENT_DLL
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CPhysCannonEffect class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+class CPhysCannonEffect
+{
+public:
+ CPhysCannonEffect( void ) : m_vecColor( 255, 255, 255 ), m_bVisible( true ), m_nAttachment( -1 ) {};
+
+ void SetAttachment( int attachment ) { m_nAttachment = attachment; }
+ int GetAttachment( void ) const { return m_nAttachment; }
+
+ void SetVisible( bool visible = true ) { m_bVisible = visible; }
+ int IsVisible( void ) const { return m_bVisible; }
+
+ void SetColor( const Vector &color ) { m_vecColor = color; }
+ const Vector &GetColor( void ) const { return m_vecColor; }
+
+ bool SetMaterial( const char *materialName )
+ {
+ m_hMaterial.Init( materialName, TEXTURE_GROUP_CLIENT_EFFECTS );
+ return ( m_hMaterial != NULL );
+ }
+
+ CMaterialReference &GetMaterial( void ) { return m_hMaterial; }
+
+ CInterpolatedValue &GetAlpha( void ) { return m_Alpha; }
+ CInterpolatedValue &GetScale( void ) { return m_Scale; }
+
+private:
+ CInterpolatedValue m_Alpha;
+ CInterpolatedValue m_Scale;
+
+ Vector m_vecColor;
+ bool m_bVisible;
+ int m_nAttachment;
+ CMaterialReference m_hMaterial;
+};
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CPhysCannonEffectBeam class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+class CPhysCannonEffectBeam
+{
+public:
+ CPhysCannonEffectBeam( void ) : m_pBeam( NULL ) {};
+
+ ~CPhysCannonEffectBeam( void )
+ {
+ Release();
+ }
+
+ void Release( void )
+ {
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->flags = 0;
+ m_pBeam->die = gpGlobals->curtime - 1;
+
+ m_pBeam = NULL;
+ }
+ }
+
+ void Init( int startAttachment, int endAttachment, CBaseEntity *pEntity, bool firstPerson )
+ {
+ if ( m_pBeam != NULL )
+ return;
+
+ BeamInfo_t beamInfo;
+
+ beamInfo.m_pStartEnt = pEntity;
+ beamInfo.m_nStartAttachment = startAttachment;
+ beamInfo.m_pEndEnt = pEntity;
+ beamInfo.m_nEndAttachment = endAttachment;
+ beamInfo.m_nType = TE_BEAMPOINTS;
+ beamInfo.m_vecStart = vec3_origin;
+ beamInfo.m_vecEnd = vec3_origin;
+
+ beamInfo.m_pszModelName = ( firstPerson ) ? PHYSCANNON_BEAM_SPRITE_NOZ : PHYSCANNON_BEAM_SPRITE;
+
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+
+ if ( firstPerson )
+ {
+ beamInfo.m_flWidth = 0.0f;
+ beamInfo.m_flEndWidth = 4.0f;
+ }
+ else
+ {
+ beamInfo.m_flWidth = 0.5f;
+ beamInfo.m_flEndWidth = 2.0f;
+ }
+
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 16;
+ beamInfo.m_flBrightness = 255.0;
+ beamInfo.m_flSpeed = 150.0f;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 30.0;
+ beamInfo.m_flRed = 255.0;
+ beamInfo.m_flGreen = 255.0;
+ beamInfo.m_flBlue = 255.0;
+ beamInfo.m_nSegments = 8;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = FBEAM_FOREVER;
+
+ m_pBeam = beams->CreateBeamEntPoint( beamInfo );
+ }
+
+ void SetVisible( bool state = true )
+ {
+ if ( m_pBeam == NULL )
+ return;
+
+ m_pBeam->brightness = ( state ) ? 255.0f : 0.0f;
+ }
+
+private:
+ Beam_t *m_pBeam;
+};
+
+#endif
+
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+// CWeaponPhysCannon class
+//----------------------------------------------------------------------------------------------------------------------------------------------------------
+
+#ifdef CLIENT_DLL
+#define CWeaponPhysCannon C_WeaponPhysCannon
+#endif
+
+class CWeaponPhysCannon : public CBaseHL2MPCombatWeapon
+{
+public:
+ DECLARE_CLASS( CWeaponPhysCannon, CBaseHL2MPCombatWeapon );
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponPhysCannon( void );
+
+ void Drop( const Vector &vecVelocity );
+ void Precache();
+ virtual void OnRestore();
+ virtual void StopLoopingSounds();
+ virtual void UpdateOnRemove(void);
+ void PrimaryAttack();
+ void SecondaryAttack();
+ void WeaponIdle();
+ void ItemPreFrame();
+ void ItemPostFrame();
+
+ void ForceDrop( void );
+ bool DropIfEntityHeld( CBaseEntity *pTarget ); // Drops its held entity if it matches the entity passed in
+ CGrabController &GetGrabController() { return m_grabController; }
+
+ bool CanHolster( void );
+ bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ bool Deploy( void );
+
+ bool HasAnyAmmo( void ) { return true; }
+
+ virtual void SetViewModel( void );
+ virtual const char *GetShootSound( int iIndex ) const;
+
+#ifndef CLIENT_DLL
+ CNetworkQAngle ( m_attachedAnglesPlayerSpace );
+#else
+ QAngle m_attachedAnglesPlayerSpace;
+#endif
+
+ CNetworkVector ( m_attachedPositionObjectSpace );
+
+ CNetworkHandle( CBaseEntity, m_hAttachedObject );
+
+ EHANDLE m_hOldAttachedObject;
+
+protected:
+ enum FindObjectResult_t
+ {
+ OBJECT_FOUND = 0,
+ OBJECT_NOT_FOUND,
+ OBJECT_BEING_DETACHED,
+ };
+
+ void DoEffect( int effectType, Vector *pos = NULL );
+
+ void OpenElements( void );
+ void CloseElements( void );
+
+ // Pickup and throw objects.
+ bool CanPickupObject( CBaseEntity *pTarget );
+ void CheckForTarget( void );
+
+#ifndef CLIENT_DLL
+ bool AttachObject( CBaseEntity *pObject, const Vector &vPosition );
+ FindObjectResult_t FindObject( void );
+ CBaseEntity *FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone );
+#endif // !CLIENT_DLL
+
+ void UpdateObject( void );
+ void DetachObject( bool playSound = true, bool wasLaunched = false );
+ void LaunchObject( const Vector &vecDir, float flForce );
+ void StartEffects( void ); // Initialize all sprites and beams
+ void StopEffects( bool stopSound = true ); // Hide all effects temporarily
+ void DestroyEffects( void ); // Destroy all sprites and beams
+
+ // Punt objects - this is pointing at an object in the world and applying a force to it.
+ void PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
+ void PuntVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr );
+
+ // Velocity-based throw common to punt and launch code.
+ void ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward );
+
+ // Physgun effects
+ void DoEffectClosed( void );
+ void DoEffectReady( void );
+ void DoEffectHolding( void );
+ void DoEffectLaunch( Vector *pos );
+ void DoEffectNone( void );
+ void DoEffectIdle( void );
+
+ // Trace length
+ float TraceLength();
+
+ // Sprite scale factor
+ float SpriteScaleFactor();
+
+ float GetLoadPercentage();
+ CSoundPatch *GetMotorSound( void );
+
+ void DryFire( void );
+ void PrimaryFireEffect( void );
+
+#ifndef CLIENT_DLL
+ // What happens when the physgun picks up something
+ void Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason );
+#endif // !CLIENT_DLL
+
+#ifdef CLIENT_DLL
+
+ enum EffectType_t
+ {
+ PHYSCANNON_CORE = 0,
+
+ PHYSCANNON_BLAST,
+
+ PHYSCANNON_GLOW1, // Must be in order!
+ PHYSCANNON_GLOW2,
+ PHYSCANNON_GLOW3,
+ PHYSCANNON_GLOW4,
+ PHYSCANNON_GLOW5,
+ PHYSCANNON_GLOW6,
+
+ PHYSCANNON_ENDCAP1, // Must be in order!
+ PHYSCANNON_ENDCAP2,
+ PHYSCANNON_ENDCAP3, // Only used in third-person!
+
+ NUM_PHYSCANNON_PARAMETERS // Must be last!
+ };
+
+#define NUM_GLOW_SPRITES ((CWeaponPhysCannon::PHYSCANNON_GLOW6-CWeaponPhysCannon::PHYSCANNON_GLOW1)+1)
+#define NUM_ENDCAP_SPRITES ((CWeaponPhysCannon::PHYSCANNON_ENDCAP3-CWeaponPhysCannon::PHYSCANNON_ENDCAP1)+1)
+
+#define NUM_PHYSCANNON_BEAMS 3
+
+ virtual int DrawModel( int flags );
+ virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
+ virtual bool IsTransparent( void );
+ virtual void OnDataChanged( DataUpdateType_t type );
+ virtual void ClientThink( void );
+
+ void ManagePredictedObject( void );
+ void DrawEffects( void );
+ void GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment );
+ void DrawEffectSprite( EffectType_t effectID );
+ inline bool IsEffectVisible( EffectType_t effectID );
+ void UpdateElementPosition( void );
+
+ // We need to render opaque and translucent pieces
+ RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TWOPASS; }
+
+ CInterpolatedValue m_ElementParameter; // Used to interpolate the position of the articulated elements
+ CPhysCannonEffect m_Parameters[NUM_PHYSCANNON_PARAMETERS]; // Interpolated parameters for the effects
+ CPhysCannonEffectBeam m_Beams[NUM_PHYSCANNON_BEAMS]; // Beams
+
+ int m_nOldEffectState; // Used for parity checks
+ bool m_bOldOpen; // Used for parity checks
+
+ void NotifyShouldTransmit( ShouldTransmitState_t state );
+
+#endif // CLIENT_DLL
+
+ int m_nChangeState; // For delayed state change of elements
+ float m_flCheckSuppressTime; // Amount of time to suppress the checking for targets
+ bool m_flLastDenySoundPlayed; // Debounce for deny sound
+ int m_nAttack2Debounce;
+
+ CNetworkVar( bool, m_bActive );
+ CNetworkVar( int, m_EffectState ); // Current state of the effects on the gun
+ CNetworkVar( bool, m_bOpen );
+
+ bool m_bResetOwnerEntity;
+
+ float m_flElementDebounce;
+
+ CSoundPatch *m_sndMotor; // Whirring sound for the gun
+
+ CGrabController m_grabController;
+
+ float m_flRepuntObjectTime;
+ EHANDLE m_hLastPuntedObject;
+
+private:
+ CWeaponPhysCannon( const CWeaponPhysCannon & );
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPhysCannon, DT_WeaponPhysCannon )
+
+BEGIN_NETWORK_TABLE( CWeaponPhysCannon, DT_WeaponPhysCannon )
+#ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bActive ) ),
+ RecvPropEHandle( RECVINFO( m_hAttachedObject ) ),
+ RecvPropVector( RECVINFO( m_attachedPositionObjectSpace ) ),
+ RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[0] ) ),
+ RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[1] ) ),
+ RecvPropFloat( RECVINFO( m_attachedAnglesPlayerSpace[2] ) ),
+ RecvPropInt( RECVINFO( m_EffectState ) ),
+ RecvPropBool( RECVINFO( m_bOpen ) ),
+#else
+ SendPropBool( SENDINFO( m_bActive ) ),
+ SendPropEHandle( SENDINFO( m_hAttachedObject ) ),
+ SendPropVector(SENDINFO( m_attachedPositionObjectSpace ), -1, SPROP_COORD),
+ SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 0 ), 11 ),
+ SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 1 ), 11 ),
+ SendPropAngle( SENDINFO_VECTORELEM(m_attachedAnglesPlayerSpace, 2 ), 11 ),
+ SendPropInt( SENDINFO( m_EffectState ) ),
+ SendPropBool( SENDINFO( m_bOpen ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponPhysCannon )
+ DEFINE_PRED_FIELD( m_EffectState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bOpen, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_physcannon, CWeaponPhysCannon );
+PRECACHE_WEAPON_REGISTER( weapon_physcannon );
+
+#ifndef CLIENT_DLL
+
+acttable_t CWeaponPhysCannon::m_acttable[] =
+{
+ { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false },
+ { ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false },
+ { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false },
+ { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false },
+ { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false },
+ { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false },
+ { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponPhysCannon);
+
+#endif
+
+
+enum
+{
+ ELEMENT_STATE_NONE = -1,
+ ELEMENT_STATE_OPEN,
+ ELEMENT_STATE_CLOSED,
+};
+
+enum
+{
+ EFFECT_NONE,
+ EFFECT_CLOSED,
+ EFFECT_READY,
+ EFFECT_HOLDING,
+ EFFECT_LAUNCH,
+};
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CWeaponPhysCannon::CWeaponPhysCannon( void )
+{
+ m_bOpen = false;
+ m_nChangeState = ELEMENT_STATE_NONE;
+ m_flCheckSuppressTime = 0.0f;
+ m_EffectState = (int)EFFECT_NONE;
+ m_flLastDenySoundPlayed = false;
+
+#ifdef CLIENT_DLL
+ m_nOldEffectState = EFFECT_NONE;
+ m_bOldOpen = false;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::Precache( void )
+{
+ PrecacheModel( PHYSCANNON_BEAM_SPRITE );
+ PrecacheModel( PHYSCANNON_BEAM_SPRITE_NOZ );
+
+ PrecacheScriptSound( "Weapon_PhysCannon.HoldSound" );
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Restore
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::OnRestore()
+{
+ BaseClass::OnRestore();
+ m_grabController.OnRestore();
+
+ // Tracker 8106: Physcannon effects disappear through level transition, so
+ // just recreate any effects here
+ if ( m_EffectState != EFFECT_NONE )
+ {
+ DoEffect( m_EffectState, NULL );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// On Remove
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::UpdateOnRemove(void)
+{
+ DestroyEffects( );
+ BaseClass::UpdateOnRemove();
+}
+
+#ifdef CLIENT_DLL
+void CWeaponPhysCannon::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
+ StartEffects();
+ }
+
+ if ( GetOwner() == NULL )
+ {
+ if ( m_hAttachedObject )
+ {
+ m_hAttachedObject->VPhysicsDestroyObject();
+ }
+
+ if ( m_hOldAttachedObject )
+ {
+ m_hOldAttachedObject->VPhysicsDestroyObject();
+ }
+ }
+
+ // Update effect state when out of parity with the server
+ if ( m_nOldEffectState != m_EffectState )
+ {
+ DoEffect( m_EffectState );
+ m_nOldEffectState = m_EffectState;
+ }
+
+ // Update element state when out of parity
+ if ( m_bOldOpen != m_bOpen )
+ {
+ if ( m_bOpen )
+ {
+ m_ElementParameter.InitFromCurrent( 1.0f, 0.2f, INTERP_SPLINE );
+ }
+ else
+ {
+ m_ElementParameter.InitFromCurrent( 0.0f, 0.5f, INTERP_SPLINE );
+ }
+
+ m_bOldOpen = (bool) m_bOpen;
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Sprite scale factor
+//-----------------------------------------------------------------------------
+inline float CWeaponPhysCannon::SpriteScaleFactor()
+{
+ return 1.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::Deploy( void )
+{
+ CloseElements();
+ DoEffect( EFFECT_READY );
+
+ bool bReturn = BaseClass::Deploy();
+
+ m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner )
+ {
+ pOwner->SetNextAttack( gpGlobals->curtime );
+ }
+
+ return bReturn;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::SetViewModel( void )
+{
+ BaseClass::SetViewModel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Force the cannon to drop anything it's carrying
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ForceDrop( void )
+{
+ CloseElements();
+ DetachObject();
+ StopEffects();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Drops its held entity if it matches the entity passed in
+// Input : *pTarget -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::DropIfEntityHeld( CBaseEntity *pTarget )
+{
+ if ( pTarget == NULL )
+ return false;
+
+ CBaseEntity *pHeld = m_grabController.GetAttached();
+
+ if ( pHeld == NULL )
+ return false;
+
+ if ( pHeld == pTarget )
+ {
+ ForceDrop();
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::Drop( const Vector &vecVelocity )
+{
+ ForceDrop();
+
+#ifndef CLIENT_DLL
+ UTIL_Remove( this );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::CanHolster( void )
+{
+ //Don't holster this weapon if we're holding onto something
+ if ( m_bActive )
+ return false;
+
+ return BaseClass::CanHolster();
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ //Don't holster this weapon if we're holding onto something
+ if ( m_bActive )
+ return false;
+
+ ForceDrop();
+ DestroyEffects();
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DryFire( void )
+{
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ WeaponSound( EMPTY );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::PrimaryFireEffect( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ pOwner->ViewPunch( QAngle(-6, SharedRandomInt( "physcannonfire", -2,2) ,0) );
+
+#ifndef CLIENT_DLL
+ color32 white = { 245, 245, 255, 32 };
+ UTIL_ScreenFade( pOwner, white, 0.1f, 0.0f, FFADE_IN );
+#endif
+
+ WeaponSound( SINGLE );
+}
+
+#define MAX_KNOCKBACK_FORCE 128
+
+//-----------------------------------------------------------------------------
+// Punt non-physics
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::PuntNonVPhysics( CBaseEntity *pEntity, const Vector &forward, trace_t &tr )
+{
+ if ( m_hLastPuntedObject == pEntity && gpGlobals->curtime < m_flRepuntObjectTime )
+ return;
+
+#ifndef CLIENT_DLL
+ CTakeDamageInfo info;
+
+ info.SetAttacker( GetOwner() );
+ info.SetInflictor( this );
+ info.SetDamage( 1.0f );
+ info.SetDamageType( DMG_CRUSH | DMG_PHYSGUN );
+ info.SetDamageForce( forward ); // Scale?
+ info.SetDamagePosition( tr.endpos );
+
+ m_hLastPuntedObject = pEntity;
+ m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
+
+ pEntity->DispatchTraceAttack( info, forward, &tr );
+
+ ApplyMultiDamage();
+
+ //Explosion effect
+ DoEffect( EFFECT_LAUNCH, &tr.endpos );
+#endif
+
+ PrimaryFireEffect();
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+ m_nChangeState = ELEMENT_STATE_CLOSED;
+ m_flElementDebounce = gpGlobals->curtime + 0.5f;
+ m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
+}
+
+
+#ifndef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// What happens when the physgun picks up something
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::Physgun_OnPhysGunPickup( CBaseEntity *pEntity, CBasePlayer *pOwner, PhysGunPickup_t reason )
+{
+ // If the target is debris, convert it to non-debris
+ if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_DEBRIS )
+ {
+ // Interactive debris converts back to debris when it comes to rest
+ pEntity->SetCollisionGroup( COLLISION_GROUP_INTERACTIVE_DEBRIS );
+ }
+
+ Pickup_OnPhysGunPickup( pEntity, pOwner, reason );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Punt vphysics
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::PuntVPhysics( CBaseEntity *pEntity, const Vector &vecForward, trace_t &tr )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+
+ if ( m_hLastPuntedObject == pEntity && gpGlobals->curtime < m_flRepuntObjectTime )
+ return;
+
+ m_hLastPuntedObject = pEntity;
+ m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
+
+#ifndef CLIENT_DLL
+ CTakeDamageInfo info;
+
+ Vector forward = vecForward;
+
+ info.SetAttacker( GetOwner() );
+ info.SetInflictor( this );
+ info.SetDamage( 0.0f );
+ info.SetDamageType( DMG_PHYSGUN );
+ pEntity->DispatchTraceAttack( info, forward, &tr );
+ ApplyMultiDamage();
+
+
+ if ( Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON ) )
+ {
+ IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT];
+ int listCount = pEntity->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) );
+ if ( !listCount )
+ {
+ //FIXME: Do we want to do this if there's no physics object?
+ Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
+ DryFire();
+ return;
+ }
+
+ if( forward.z < 0 )
+ {
+ //reflect, but flatten the trajectory out a bit so it's easier to hit standing targets
+ forward.z *= -0.65f;
+ }
+
+ // NOTE: Do this first to enable motion (if disabled) - so forces will work
+ // Tell the object it's been punted
+ Physgun_OnPhysGunPickup( pEntity, pOwner, PUNTED_BY_CANNON );
+
+ // don't push vehicles that are attached to the world via fixed constraints
+ // they will just wiggle...
+ if ( (pList[0]->GetGameFlags() & FVPHYSICS_CONSTRAINT_STATIC) && pEntity->GetServerVehicle() )
+ {
+ forward.Init();
+ }
+
+ if ( !Pickup_ShouldPuntUseLaunchForces( pEntity, PHYSGUN_FORCE_PUNTED ) )
+ {
+ int i;
+
+ // limit mass to avoid punting REALLY huge things
+ float totalMass = 0;
+ for ( i = 0; i < listCount; i++ )
+ {
+ totalMass += pList[i]->GetMass();
+ }
+ float maxMass = 250;
+ IServerVehicle *pVehicle = pEntity->GetServerVehicle();
+ if ( pVehicle )
+ {
+ maxMass *= 2.5; // 625 for vehicles
+ }
+ float mass = MIN(totalMass, maxMass); // max 250kg of additional force
+
+ // Put some spin on the object
+ for ( i = 0; i < listCount; i++ )
+ {
+ const float hitObjectFactor = 0.5f;
+ const float otherObjectFactor = 1.0f - hitObjectFactor;
+ // Must be light enough
+ float ratio = pList[i]->GetMass() / totalMass;
+ if ( pList[i] == pEntity->VPhysicsGetObject() )
+ {
+ ratio += hitObjectFactor;
+ ratio = MIN(ratio,1.0f);
+ }
+ else
+ {
+ ratio *= otherObjectFactor;
+ }
+ pList[i]->ApplyForceCenter( forward * 15000.0f * ratio );
+ pList[i]->ApplyForceOffset( forward * mass * 600.0f * ratio, tr.endpos );
+ }
+ }
+ else
+ {
+ ApplyVelocityBasedForce( pEntity, vecForward );
+ }
+ }
+
+#endif
+ // Add recoil
+ QAngle recoil = QAngle( random->RandomFloat( 1.0f, 2.0f ), random->RandomFloat( -1.0f, 1.0f ), 0 );
+ pOwner->ViewPunch( recoil );
+
+ //Explosion effect
+ DoEffect( EFFECT_LAUNCH, &tr.endpos );
+
+ PrimaryFireEffect();
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+ m_nChangeState = ELEMENT_STATE_CLOSED;
+ m_flElementDebounce = gpGlobals->curtime + 0.5f;
+ m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
+
+ // Don't allow the gun to regrab a thrown object!!
+ m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Applies velocity-based forces to throw the entity. This code is
+// called from both punt and launch carried code.
+// ASSUMES: that pEntity is a vphysics entity.
+// Input : -
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ApplyVelocityBasedForce( CBaseEntity *pEntity, const Vector &forward )
+{
+#ifndef CLIENT_DLL
+ IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
+ Assert(pPhysicsObject); // Shouldn't ever get here with a non-vphysics object.
+ if (!pPhysicsObject)
+ return;
+
+ float flForceMax = physcannon_maxforce.GetFloat();
+ float flForce = flForceMax;
+
+ float mass = pPhysicsObject->GetMass();
+ if (mass > 100)
+ {
+ mass = MIN(mass, 1000);
+ float flForceMin = physcannon_minforce.GetFloat();
+ flForce = SimpleSplineRemapVal(mass, 100, 600, flForceMax, flForceMin);
+ }
+
+ Vector vVel = forward * flForce;
+ // FIXME: Josh needs to put a real value in for PHYSGUN_FORCE_PUNTED
+ AngularImpulse aVel = Pickup_PhysGunLaunchAngularImpulse( pEntity, PHYSGUN_FORCE_PUNTED );
+
+ pPhysicsObject->AddVelocity( &vVel, &aVel );
+
+#endif
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Trace length
+//-----------------------------------------------------------------------------
+float CWeaponPhysCannon::TraceLength()
+{
+ return physcannon_tracelength.GetFloat();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// This mode is a toggle. Primary fire one time to pick up a physics object.
+// With an object held, click primary fire again to drop object.
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::PrimaryAttack( void )
+{
+ if( m_flNextPrimaryAttack > gpGlobals->curtime )
+ return;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ if( m_bActive )
+ {
+ // Punch the object being held!!
+ Vector forward;
+ pOwner->EyeVectors( &forward );
+
+ // Validate the item is within punt range
+ CBaseEntity *pHeld = m_grabController.GetAttached();
+ Assert( pHeld != NULL );
+
+ if ( pHeld != NULL )
+ {
+ float heldDist = ( pHeld->WorldSpaceCenter() - pOwner->WorldSpaceCenter() ).Length();
+
+ if ( heldDist > physcannon_tracelength.GetFloat() )
+ {
+ // We can't punt this yet
+ DryFire();
+ return;
+ }
+ }
+
+ LaunchObject( forward, physcannon_maxforce.GetFloat() );
+
+ PrimaryFireEffect();
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+ return;
+ }
+
+ // If not active, just issue a physics punch in the world.
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
+
+ Vector forward;
+ pOwner->EyeVectors( &forward );
+
+ // NOTE: Notice we're *not* using the mega tracelength here
+ // when you have the mega cannon. Punting has shorter range.
+ Vector start, end;
+ start = pOwner->Weapon_ShootPosition();
+ float flPuntDistance = physcannon_tracelength.GetFloat();
+ VectorMA( start, flPuntDistance, forward, end );
+
+ CTraceFilterNoOwnerTest filter( pOwner, COLLISION_GROUP_NONE );
+ trace_t tr;
+ UTIL_TraceHull( start, end, -Vector(8,8,8), Vector(8,8,8), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+ bool bValid = true;
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
+ {
+ bValid = false;
+ }
+ else if ( (pEntity->GetMoveType() != MOVETYPE_VPHYSICS) && ( pEntity->m_takedamage == DAMAGE_NO ) )
+ {
+ bValid = false;
+ }
+
+ // If the entity we've hit is invalid, try a traceline instead
+ if ( !bValid )
+ {
+ UTIL_TraceLine( start, end, MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+ if ( tr.fraction == 1 || !tr.m_pEnt || tr.m_pEnt->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
+ {
+ // Play dry-fire sequence
+ DryFire();
+ return;
+ }
+
+ pEntity = tr.m_pEnt;
+ }
+
+ // See if we hit something
+ if ( pEntity->GetMoveType() != MOVETYPE_VPHYSICS )
+ {
+ if ( pEntity->m_takedamage == DAMAGE_NO )
+ {
+ DryFire();
+ return;
+ }
+
+ if( GetOwner()->IsPlayer() )
+ {
+ // Don't let the player zap any NPC's except regular antlions and headcrabs.
+ if( pEntity->IsPlayer() )
+ {
+ DryFire();
+ return;
+ }
+ }
+
+ PuntNonVPhysics( pEntity, forward, tr );
+ }
+ else
+ {
+ if ( pEntity->VPhysicsIsFlesh( ) )
+ {
+ DryFire();
+ return;
+ }
+ PuntVPhysics( pEntity, forward, tr );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Click secondary attack whilst holding an object to hurl it.
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::SecondaryAttack( void )
+{
+#ifndef CLIENT_DLL
+ if ( m_flNextSecondaryAttack > gpGlobals->curtime )
+ return;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ // See if we should drop a held item
+ if ( ( m_bActive ) && ( pOwner->m_afButtonPressed & IN_ATTACK2 ) )
+ {
+ // Drop the held object
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
+
+ DetachObject();
+
+ DoEffect( EFFECT_READY );
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ }
+ else
+ {
+ // Otherwise pick it up
+ FindObjectResult_t result = FindObject();
+ switch ( result )
+ {
+ case OBJECT_FOUND:
+ WeaponSound( SPECIAL1 );
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
+
+ // We found an object. Debounce the button
+ m_nAttack2Debounce |= pOwner->m_nButtons;
+ break;
+
+ case OBJECT_NOT_FOUND:
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.1f;
+ CloseElements();
+ break;
+
+ case OBJECT_BEING_DETACHED:
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.01f;
+ break;
+ }
+
+ DoEffect( EFFECT_HOLDING );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::WeaponIdle( void )
+{
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ if ( m_bActive )
+ {
+ //Shake when holding an item
+ SendWeaponAnim( ACT_VM_RELOAD );
+ }
+ else
+ {
+ //Otherwise idle simply
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ }
+}
+
+#ifndef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObject -
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::AttachObject( CBaseEntity *pObject, const Vector &vPosition )
+{
+
+ if ( m_bActive )
+ return false;
+
+ if ( CanPickupObject( pObject ) == false )
+ return false;
+
+ m_grabController.SetIgnorePitch( false );
+ m_grabController.SetAngleAlignment( 0 );
+
+ IPhysicsObject *pPhysics = pObject->VPhysicsGetObject();
+
+ // Must be valid
+ if ( !pPhysics )
+ return false;
+
+ CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( GetOwner() );
+
+ m_bActive = true;
+ if( pOwner )
+ {
+ // NOTE: This can change the mass; so it must be done before max speed setting
+ Physgun_OnPhysGunPickup( pObject, pOwner, PICKED_UP_BY_CANNON );
+ }
+
+ // NOTE :This must happen after OnPhysGunPickup because that can change the mass
+ m_grabController.AttachEntity( pOwner, pObject, pPhysics, false, vPosition, false );
+ m_hAttachedObject = pObject;
+ m_attachedPositionObjectSpace = m_grabController.m_attachedPositionObjectSpace;
+ m_attachedAnglesPlayerSpace = m_grabController.m_attachedAnglesPlayerSpace;
+
+ m_bResetOwnerEntity = false;
+
+ if ( m_hAttachedObject->GetOwnerEntity() == NULL )
+ {
+ m_hAttachedObject->SetOwnerEntity( pOwner );
+ m_bResetOwnerEntity = true;
+ }
+
+/* if( pOwner )
+ {
+ pOwner->EnableSprint( false );
+
+ float loadWeight = ( 1.0f - GetLoadPercentage() );
+ float maxSpeed = hl2_walkspeed.GetFloat() + ( ( hl2_normspeed.GetFloat() - hl2_walkspeed.GetFloat() ) * loadWeight );
+
+ //Msg( "Load perc: %f -- Movement speed: %f/%f\n", loadWeight, maxSpeed, hl2_normspeed.GetFloat() );
+ pOwner->SetMaxSpeed( maxSpeed );
+ }*/
+
+ // Don't drop again for a slight delay, in case they were pulling objects near them
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.4f;
+
+ DoEffect( EFFECT_HOLDING );
+ OpenElements();
+
+ if ( GetMotorSound() )
+ {
+ (CSoundEnvelopeController::GetController()).Play( GetMotorSound(), 0.0f, 50 );
+ (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 100, 0.5f );
+ (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.8f, 0.5f );
+ }
+
+
+
+ return true;
+}
+
+CWeaponPhysCannon::FindObjectResult_t CWeaponPhysCannon::FindObject( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ Assert( pPlayer );
+ if ( pPlayer == NULL )
+ return OBJECT_NOT_FOUND;
+
+ Vector forward;
+ pPlayer->EyeVectors( &forward );
+
+ // Setup our positions
+ Vector start = pPlayer->Weapon_ShootPosition();
+ float testLength = TraceLength() * 4.0f;
+ Vector end = start + forward * testLength;
+
+ // Try to find an object by looking straight ahead
+ trace_t tr;
+ CTraceFilterNoOwnerTest filter( pPlayer, COLLISION_GROUP_NONE );
+ UTIL_TraceLine( start, end, MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+
+ // Try again with a hull trace
+ if ( ( tr.fraction == 1.0 ) || ( tr.m_pEnt == NULL ) || ( tr.m_pEnt->IsWorld() ) )
+ {
+ UTIL_TraceHull( start, end, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+ }
+
+ CBaseEntity *pEntity = tr.m_pEnt ? tr.m_pEnt->GetRootMoveParent() : NULL;
+ bool bAttach = false;
+ bool bPull = false;
+
+ // If we hit something, pick it up or pull it
+ if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt ) && ( tr.m_pEnt->IsWorld() == false ) )
+ {
+ // Attempt to attach if within range
+ if ( tr.fraction <= 0.25f )
+ {
+ bAttach = true;
+ }
+ else if ( tr.fraction > 0.25f )
+ {
+ bPull = true;
+ }
+ }
+
+ // Find anything within a general cone in front
+ CBaseEntity *pConeEntity = NULL;
+
+ if (!bAttach && !bPull)
+ {
+ pConeEntity = FindObjectInCone( start, forward, physcannon_cone.GetFloat() );
+ }
+
+ if ( pConeEntity )
+ {
+ pEntity = pConeEntity;
+
+ // If the object is near, grab it. Else, pull it a bit.
+ if ( pEntity->WorldSpaceCenter().DistToSqr( start ) <= (testLength * testLength) )
+ {
+ bAttach = true;
+ }
+ else
+ {
+ bPull = true;
+ }
+ }
+
+ if ( CanPickupObject( pEntity ) == false )
+ {
+ // Make a noise to signify we can't pick this up
+ if ( !m_flLastDenySoundPlayed )
+ {
+ m_flLastDenySoundPlayed = true;
+ WeaponSound( SPECIAL3 );
+ }
+
+ return OBJECT_NOT_FOUND;
+ }
+
+ // Check to see if the object is constrained + needs to be ripped off...
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( !Pickup_OnAttemptPhysGunPickup( pEntity, pOwner, PICKED_UP_BY_CANNON ) )
+ return OBJECT_BEING_DETACHED;
+
+ if ( bAttach )
+ {
+ return AttachObject( pEntity, tr.endpos ) ? OBJECT_FOUND : OBJECT_NOT_FOUND;
+ }
+
+ if ( !bPull )
+ return OBJECT_NOT_FOUND;
+
+ // FIXME: This needs to be run through the CanPickupObject logic
+ IPhysicsObject *pObj = pEntity->VPhysicsGetObject();
+ if ( !pObj )
+ return OBJECT_NOT_FOUND;
+
+ // If we're too far, simply start to pull the object towards us
+ Vector pullDir = start - pEntity->WorldSpaceCenter();
+ VectorNormalize( pullDir );
+ pullDir *= physcannon_pullforce.GetFloat();
+
+ float mass = PhysGetEntityMass( pEntity );
+ if ( mass < 50.0f )
+ {
+ pullDir *= (mass + 0.5) * (1/50.0f);
+ }
+
+ // Nudge it towards us
+ pObj->ApplyForceCenter( pullDir );
+ return OBJECT_NOT_FOUND;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponPhysCannon::FindObjectInCone( const Vector &vecOrigin, const Vector &vecDir, float flCone )
+{
+ // Find the nearest physics-based item in a cone in front of me.
+ CBaseEntity *list[256];
+ float flNearestDist = TraceLength() + 1.0;
+ Vector mins = vecOrigin - Vector( flNearestDist, flNearestDist, flNearestDist );
+ Vector maxs = vecOrigin + Vector( flNearestDist, flNearestDist, flNearestDist );
+
+ CBaseEntity *pNearest = NULL;
+
+ int count = UTIL_EntitiesInBox( list, 256, mins, maxs, 0 );
+ for( int i = 0 ; i < count ; i++ )
+ {
+ if ( !list[ i ]->VPhysicsGetObject() )
+ continue;
+
+ // Closer than other objects
+ Vector los = ( list[ i ]->WorldSpaceCenter() - vecOrigin );
+ float flDist = VectorNormalize( los );
+ if( flDist >= flNearestDist )
+ continue;
+
+ // Cull to the cone
+ if ( DotProduct( los, vecDir ) <= flCone )
+ continue;
+
+ // Make sure it isn't occluded!
+ trace_t tr;
+ CTraceFilterNoOwnerTest filter( GetOwner(), COLLISION_GROUP_NONE );
+ UTIL_TraceLine( vecOrigin, list[ i ]->WorldSpaceCenter(), MASK_SHOT|CONTENTS_GRATE, &filter, &tr );
+ if( tr.m_pEnt == list[ i ] )
+ {
+ flNearestDist = flDist;
+ pNearest = list[ i ];
+ }
+ }
+
+ return pNearest;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CGrabController::UpdateObject( CBasePlayer *pPlayer, float flError )
+{
+ CBaseEntity *pEntity = GetAttached();
+ if ( !pEntity )
+ return false;
+ if ( ComputeError() > flError )
+ return false;
+ if ( pPlayer->GetGroundEntity() == pEntity )
+ return false;
+ if (!pEntity->VPhysicsGetObject() )
+ return false;
+
+ //Adrian: Oops, our object became motion disabled, let go!
+ IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
+ if ( pPhys && pPhys->IsMoveable() == false )
+ {
+ return false;
+ }
+
+ if ( m_frameCount == gpGlobals->framecount )
+ {
+ return true;
+ }
+ m_frameCount = gpGlobals->framecount;
+ Vector forward, right, up;
+ QAngle playerAngles = pPlayer->EyeAngles();
+
+ float pitch = AngleDistance(playerAngles.x,0);
+ playerAngles.x = clamp( pitch, -75, 75 );
+ AngleVectors( playerAngles, &forward, &right, &up );
+
+ // Now clamp a sphere of object radius at end to the player's bbox
+ Vector radial = physcollision->CollideGetExtent( pPhys->GetCollide(), vec3_origin, pEntity->GetAbsAngles(), -forward );
+ Vector player2d = pPlayer->CollisionProp()->OBBMaxs();
+ float playerRadius = player2d.Length2D();
+ float flDot = DotProduct( forward, radial );
+
+ float radius = playerRadius + fabs( flDot );
+
+ float distance = 24 + ( radius * 2.0f );
+
+ Vector start = pPlayer->Weapon_ShootPosition();
+ Vector end = start + ( forward * distance );
+
+ trace_t tr;
+ CTraceFilterSkipTwoEntities traceFilter( pPlayer, pEntity, COLLISION_GROUP_NONE );
+ Ray_t ray;
+ ray.Init( start, end );
+ enginetrace->TraceRay( ray, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
+
+ if ( tr.fraction < 0.5 )
+ {
+ end = start + forward * (radius*0.5f);
+ }
+ else if ( tr.fraction <= 1.0f )
+ {
+ end = start + forward * ( distance - radius );
+ }
+
+ Vector playerMins, playerMaxs, nearest;
+ pPlayer->CollisionProp()->WorldSpaceAABB( &playerMins, &playerMaxs );
+ Vector playerLine = pPlayer->CollisionProp()->WorldSpaceCenter();
+ CalcClosestPointOnLine( end, playerLine+Vector(0,0,playerMins.z), playerLine+Vector(0,0,playerMaxs.z), nearest, NULL );
+
+ Vector delta = end - nearest;
+ float len = VectorNormalize(delta);
+ if ( len < radius )
+ {
+ end = nearest + radius * delta;
+ }
+
+ QAngle angles = TransformAnglesFromPlayerSpace( m_attachedAnglesPlayerSpace, pPlayer );
+
+ //Show overlays of radius
+ if ( g_debug_physcannon.GetBool() )
+ {
+
+#ifdef CLIENT_DLL
+
+ debugoverlay->AddBoxOverlay( end, -Vector( 2,2,2 ), Vector(2,2,2), angles, 0, 255, 255, true, 0 );
+
+ debugoverlay->AddBoxOverlay( GetAttached()->WorldSpaceCenter(),
+ -Vector( radius, radius, radius),
+ Vector( radius, radius, radius ),
+ angles,
+ 255, 255, 0,
+ true,
+ 0.0f );
+
+#else
+
+ NDebugOverlay::Box( end, -Vector( 2,2,2 ), Vector(2,2,2), 0, 255, 0, true, 0 );
+
+ NDebugOverlay::Box( GetAttached()->WorldSpaceCenter(),
+ -Vector( radius+5, radius+5, radius+5),
+ Vector( radius+5, radius+5, radius+5 ),
+ 255, 0, 0,
+ true,
+ 0.0f );
+#endif
+ }
+
+#ifndef CLIENT_DLL
+ // If it has a preferred orientation, update to ensure we're still oriented correctly.
+ Pickup_GetPreferredCarryAngles( pEntity, pPlayer, pPlayer->EntityToWorldTransform(), angles );
+
+
+ // We may be holding a prop that has preferred carry angles
+ if ( m_bHasPreferredCarryAngles )
+ {
+ matrix3x4_t tmp;
+ ComputePlayerMatrix( pPlayer, tmp );
+ angles = TransformAnglesToWorldSpace( m_vecPreferredCarryAngles, tmp );
+ }
+
+#endif
+
+ matrix3x4_t attachedToWorld;
+ Vector offset;
+ AngleMatrix( angles, attachedToWorld );
+ VectorRotate( m_attachedPositionObjectSpace, attachedToWorld, offset );
+
+ SetTargetPosition( end - offset, angles );
+
+ return true;
+}
+
+void CWeaponPhysCannon::UpdateObject( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ Assert( pPlayer );
+
+ float flError = 12;
+ if ( !m_grabController.UpdateObject( pPlayer, flError ) )
+ {
+ DetachObject();
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DetachObject( bool playSound, bool wasLaunched )
+{
+#ifndef CLIENT_DLL
+ if ( m_bActive == false )
+ return;
+
+ CHL2MP_Player *pOwner = (CHL2MP_Player *)ToBasePlayer( GetOwner() );
+ if( pOwner != NULL )
+ {
+ pOwner->EnableSprint( true );
+ pOwner->SetMaxSpeed( hl2_normspeed.GetFloat() );
+ }
+
+ CBaseEntity *pObject = m_grabController.GetAttached();
+
+ m_grabController.DetachEntity( wasLaunched );
+
+ if ( pObject != NULL )
+ {
+ Pickup_OnPhysGunDrop( pObject, pOwner, wasLaunched ? LAUNCHED_BY_CANNON : DROPPED_BY_CANNON );
+ }
+
+ // Stop our looping sound
+ if ( GetMotorSound() )
+ {
+ (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
+ (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
+ }
+
+ if ( pObject && m_bResetOwnerEntity == true )
+ {
+ pObject->SetOwnerEntity( NULL );
+ }
+
+ m_bActive = false;
+ m_hAttachedObject = NULL;
+
+
+ if ( playSound )
+ {
+ //Play the detach sound
+ WeaponSound( MELEE_MISS );
+ }
+
+#else
+
+ m_grabController.DetachEntity( wasLaunched );
+
+ if ( m_hAttachedObject )
+ {
+ m_hAttachedObject->VPhysicsDestroyObject();
+ }
+#endif
+}
+
+
+#ifdef CLIENT_DLL
+void CWeaponPhysCannon::ManagePredictedObject( void )
+{
+ CBaseEntity *pAttachedObject = m_hAttachedObject.Get();
+
+ if ( m_hAttachedObject )
+ {
+ // NOTE :This must happen after OnPhysGunPickup because that can change the mass
+ if ( pAttachedObject != GetGrabController().GetAttached() )
+ {
+ IPhysicsObject *pPhysics = pAttachedObject->VPhysicsGetObject();
+
+ if ( pPhysics == NULL )
+ {
+ solid_t tmpSolid;
+ PhysModelParseSolid( tmpSolid, m_hAttachedObject, pAttachedObject->GetModelIndex() );
+
+ pAttachedObject->VPhysicsInitNormal( SOLID_VPHYSICS, 0, false, &tmpSolid );
+ }
+
+ pPhysics = pAttachedObject->VPhysicsGetObject();
+
+ if ( pPhysics )
+ {
+ m_grabController.SetIgnorePitch( false );
+ m_grabController.SetAngleAlignment( 0 );
+
+ GetGrabController().AttachEntity( ToBasePlayer( GetOwner() ), pAttachedObject, pPhysics, false, vec3_origin, false );
+ GetGrabController().m_attachedPositionObjectSpace = m_attachedPositionObjectSpace;
+ GetGrabController().m_attachedAnglesPlayerSpace = m_attachedAnglesPlayerSpace;
+ }
+ }
+ }
+ else
+ {
+ if ( m_hOldAttachedObject && m_hOldAttachedObject->VPhysicsGetObject() )
+ {
+ GetGrabController().DetachEntity( false );
+
+ m_hOldAttachedObject->VPhysicsDestroyObject();
+ }
+ }
+
+ m_hOldAttachedObject = m_hAttachedObject;
+}
+
+#endif
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the pose parameter for the gun
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::UpdateElementPosition( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ float flElementPosition = m_ElementParameter.Interp( gpGlobals->curtime );
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ if ( pOwner != NULL )
+ {
+ CBaseViewModel *vm = pOwner->GetViewModel();
+
+ if ( vm != NULL )
+ {
+ vm->SetPoseParameter( "active", flElementPosition );
+ }
+ }
+ }
+ else
+ {
+ SetPoseParameter( "active", flElementPosition );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Think function for the client
+//-----------------------------------------------------------------------------
+
+void CWeaponPhysCannon::ClientThink( void )
+{
+ // Update our elements visually
+ UpdateElementPosition();
+
+ // Update our effects
+ DoEffectIdle();
+}
+
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ItemPreFrame()
+{
+ BaseClass::ItemPreFrame();
+
+#ifdef CLIENT_DLL
+ C_BasePlayer *localplayer = C_BasePlayer::GetLocalPlayer();
+
+ if ( localplayer && !localplayer->IsObserver() )
+ ManagePredictedObject();
+#endif
+
+ // Update the object if the weapon is switched on.
+ if( m_bActive )
+ {
+ UpdateObject();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::CheckForTarget( void )
+{
+#ifndef CLIENT_DLL
+ //See if we're suppressing this
+ if ( m_flCheckSuppressTime > gpGlobals->curtime )
+ return;
+
+ // holstered
+ if ( IsEffectActive( EF_NODRAW ) )
+ return;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( m_bActive )
+ return;
+
+ Vector aimDir;
+ pOwner->EyeVectors( &aimDir );
+
+ Vector startPos = pOwner->Weapon_ShootPosition();
+ Vector endPos;
+ VectorMA( startPos, TraceLength(), aimDir, endPos );
+
+ trace_t tr;
+ UTIL_TraceHull( startPos, endPos, -Vector(4,4,4), Vector(4,4,4), MASK_SHOT|CONTENTS_GRATE, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ if ( ( tr.fraction != 1.0f ) && ( tr.m_pEnt != NULL ) )
+ {
+ // FIXME: Try just having the elements always open when pointed at a physics object
+ if ( CanPickupObject( tr.m_pEnt ) || Pickup_ForcePhysGunOpen( tr.m_pEnt, pOwner ) )
+ // if ( ( tr.m_pEnt->VPhysicsGetObject() != NULL ) && ( tr.m_pEnt->GetMoveType() == MOVETYPE_VPHYSICS ) )
+ {
+ m_nChangeState = ELEMENT_STATE_NONE;
+ OpenElements();
+ return;
+ }
+ }
+
+ // Close the elements after a delay to prevent overact state switching
+ if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState == ELEMENT_STATE_NONE ) )
+ {
+ m_nChangeState = ELEMENT_STATE_CLOSED;
+ m_flElementDebounce = gpGlobals->curtime + 0.5f;
+ }
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Idle effect (pulsing)
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectIdle( void )
+{
+#ifdef CLIENT_DLL
+
+ StartEffects();
+
+ //if ( ShouldDrawUsingViewModel() )
+ {
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 0.075f, 0.05f ) * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 24, 32 ) );
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 3, 5 ) );
+ m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 200, 255 ) );
+ }
+
+ if ( m_EffectState != EFFECT_HOLDING )
+ {
+ // Turn beams off
+ m_Beams[0].SetVisible( false );
+ m_Beams[1].SetVisible( false );
+ m_Beams[2].SetVisible( false );
+ }
+ }
+ /*
+ else
+ {
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 0.075f, 0.05f ) * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 24, 32 ) );
+ }
+
+ // Turn on the glow sprites
+ for ( i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().SetAbsolute( random->RandomFloat( 3, 5 ) );
+ m_Parameters[i].GetAlpha().SetAbsolute( random->RandomInt( 200, 255 ) );
+ }
+
+ if ( m_EffectState != EFFECT_HOLDING )
+ {
+ // Turn beams off
+ m_Beams[0].SetVisible( false );
+ m_Beams[1].SetVisible( false );
+ m_Beams[2].SetVisible( false );
+ }
+ }
+ */
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ItemPostFrame()
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( pOwner == NULL )
+ {
+ // We found an object. Debounce the button
+ m_nAttack2Debounce = 0;
+ return;
+ }
+
+ //Check for object in pickup range
+ if ( m_bActive == false )
+ {
+ CheckForTarget();
+
+ if ( ( m_flElementDebounce < gpGlobals->curtime ) && ( m_nChangeState != ELEMENT_STATE_NONE ) )
+ {
+ if ( m_nChangeState == ELEMENT_STATE_OPEN )
+ {
+ OpenElements();
+ }
+ else if ( m_nChangeState == ELEMENT_STATE_CLOSED )
+ {
+ CloseElements();
+ }
+
+ m_nChangeState = ELEMENT_STATE_NONE;
+ }
+ }
+
+ // NOTE: Attack2 will be considered to be pressed until the first item is picked up.
+ int nAttack2Mask = pOwner->m_nButtons & (~m_nAttack2Debounce);
+ if ( nAttack2Mask & IN_ATTACK2 )
+ {
+ SecondaryAttack();
+ }
+ else
+ {
+ // Reset our debouncer
+ m_flLastDenySoundPlayed = false;
+
+ if ( m_bActive == false )
+ {
+ DoEffect( EFFECT_READY );
+ }
+ }
+
+ if (( pOwner->m_nButtons & IN_ATTACK2 ) == 0 )
+ {
+ m_nAttack2Debounce = 0;
+ }
+
+ if ( pOwner->m_nButtons & IN_ATTACK )
+ {
+ PrimaryAttack();
+ }
+ else
+ {
+ WeaponIdle();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#define PHYSCANNON_DANGER_SOUND_RADIUS 128
+
+void CWeaponPhysCannon::LaunchObject( const Vector &vecDir, float flForce )
+{
+ CBaseEntity *pObject = m_grabController.GetAttached();
+
+ if ( !(m_hLastPuntedObject == pObject && gpGlobals->curtime < m_flRepuntObjectTime) )
+ {
+ // FIRE!!!
+ if( pObject != NULL )
+ {
+ DetachObject( false, true );
+
+ m_hLastPuntedObject = pObject;
+ m_flRepuntObjectTime = gpGlobals->curtime + 0.5f;
+
+ // Launch
+ ApplyVelocityBasedForce( pObject, vecDir );
+
+ // Don't allow the gun to regrab a thrown object!!
+ m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
+
+ Vector center = pObject->WorldSpaceCenter();
+
+ //Do repulse effect
+ DoEffect( EFFECT_LAUNCH, &center );
+
+ m_hAttachedObject = NULL;
+ m_bActive = false;
+ }
+ }
+
+ // Stop our looping sound
+ if ( GetMotorSound() )
+ {
+ (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
+ (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
+ }
+
+ //Close the elements and suppress checking for a bit
+ m_nChangeState = ELEMENT_STATE_CLOSED;
+ m_flElementDebounce = gpGlobals->curtime + 0.1f;
+ m_flCheckSuppressTime = gpGlobals->curtime + 0.25f;
+}
+
+bool UTIL_IsCombineBall( CBaseEntity *pEntity );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pTarget -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::CanPickupObject( CBaseEntity *pTarget )
+{
+#ifndef CLIENT_DLL
+ if ( pTarget == NULL )
+ return false;
+
+ if ( pTarget->GetBaseAnimating() && pTarget->GetBaseAnimating()->IsDissolving() )
+ return false;
+
+ if ( pTarget->IsEFlagSet( EFL_NO_PHYSCANNON_INTERACTION ) )
+ return false;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner && pOwner->GetGroundEntity() == pTarget )
+ return false;
+
+ if ( pTarget->VPhysicsIsFlesh( ) )
+ return false;
+
+ IPhysicsObject *pObj = pTarget->VPhysicsGetObject();
+
+ if ( pObj && pObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
+ return false;
+
+ if ( UTIL_IsCombineBall( pTarget ) )
+ {
+ return CBasePlayer::CanPickupObject( pTarget, 0, 0 );
+ }
+
+ return CBasePlayer::CanPickupObject( pTarget, physcannon_maxmass.GetFloat(), 0 );
+#else
+ return false;
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::OpenElements( void )
+{
+ if ( m_bOpen )
+ return;
+
+ WeaponSound( SPECIAL2 );
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ SendWeaponAnim( ACT_VM_IDLE );
+
+ m_bOpen = true;
+
+ DoEffect( EFFECT_READY );
+
+#ifdef CLIENT
+ // Element prediction
+ m_ElementParameter.InitFromCurrent( 1.0f, 0.2f, INTERP_SPLINE );
+ m_bOldOpen = true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::CloseElements( void )
+{
+ if ( m_bOpen == false )
+ return;
+
+ WeaponSound( MELEE_HIT );
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ SendWeaponAnim( ACT_VM_IDLE );
+
+ m_bOpen = false;
+
+ if ( GetMotorSound() )
+ {
+ (CSoundEnvelopeController::GetController()).SoundChangeVolume( GetMotorSound(), 0.0f, 1.0f );
+ (CSoundEnvelopeController::GetController()).SoundChangePitch( GetMotorSound(), 50, 1.0f );
+ }
+
+ DoEffect( EFFECT_CLOSED );
+
+#ifdef CLIENT
+ // Element prediction
+ m_ElementParameter.InitFromCurrent( 0.0f, 0.5f, INTERP_SPLINE );
+ m_bOldOpen = false;
+#endif
+}
+
+#define PHYSCANNON_MAX_MASS 500
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CWeaponPhysCannon::GetLoadPercentage( void )
+{
+ float loadWeight = m_grabController.GetLoadWeight();
+ loadWeight /= physcannon_maxmass.GetFloat();
+ loadWeight = clamp( loadWeight, 0.0f, 1.0f );
+ return loadWeight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CSoundPatch
+//-----------------------------------------------------------------------------
+CSoundPatch *CWeaponPhysCannon::GetMotorSound( void )
+{
+ if ( m_sndMotor == NULL )
+ {
+ CPASAttenuationFilter filter( this );
+
+ m_sndMotor = (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "Weapon_PhysCannon.HoldSound", ATTN_NORM );
+ }
+
+ return m_sndMotor;
+}
+
+
+//-----------------------------------------------------------------------------
+// Shuts down sounds
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::StopLoopingSounds()
+{
+ if ( m_sndMotor != NULL )
+ {
+ (CSoundEnvelopeController::GetController()).SoundDestroy( m_sndMotor );
+ m_sndMotor = NULL;
+ }
+
+#ifndef CLIENT_DLL
+ BaseClass::StopLoopingSounds();
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DestroyEffects( void )
+{
+#ifdef CLIENT_DLL
+
+ // Free our beams
+ m_Beams[0].Release();
+ m_Beams[1].Release();
+ m_Beams[2].Release();
+
+#endif
+
+ // Stop everything
+ StopEffects();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::StopEffects( bool stopSound )
+{
+ // Turn off our effect state
+ DoEffect( EFFECT_NONE );
+
+#ifndef CLIENT_DLL
+ //Shut off sounds
+ if ( stopSound && GetMotorSound() != NULL )
+ {
+ (CSoundEnvelopeController::GetController()).SoundFadeOut( GetMotorSound(), 0.1f );
+ }
+#endif // !CLIENT_DLL
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::StartEffects( void )
+{
+#ifdef CLIENT_DLL
+
+ // ------------------------------------------
+ // Core
+ // ------------------------------------------
+
+ if ( m_Parameters[PHYSCANNON_CORE].GetMaterial() == NULL )
+ {
+ m_Parameters[PHYSCANNON_CORE].GetScale().Init( 0.0f, 1.0f, 0.1f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().Init( 255.0f, 255.0f, 0.1f );
+ m_Parameters[PHYSCANNON_CORE].SetAttachment( 1 );
+
+ if ( m_Parameters[PHYSCANNON_CORE].SetMaterial( PHYSCANNON_CENTER_GLOW ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // Blast
+ // ------------------------------------------
+
+ if ( m_Parameters[PHYSCANNON_BLAST].GetMaterial() == NULL )
+ {
+ m_Parameters[PHYSCANNON_BLAST].GetScale().Init( 0.0f, 1.0f, 0.1f );
+ m_Parameters[PHYSCANNON_BLAST].GetAlpha().Init( 255.0f, 255.0f, 0.1f );
+ m_Parameters[PHYSCANNON_BLAST].SetAttachment( 1 );
+ m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
+
+ if ( m_Parameters[PHYSCANNON_BLAST].SetMaterial( PHYSCANNON_BLAST_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // Glows
+ // ------------------------------------------
+
+ const char *attachNamesGlowThirdPerson[NUM_GLOW_SPRITES] =
+ {
+ "fork1m",
+ "fork1t",
+ "fork2m",
+ "fork2t",
+ "fork3m",
+ "fork3t",
+ };
+
+ const char *attachNamesGlow[NUM_GLOW_SPRITES] =
+ {
+ "fork1b",
+ "fork1m",
+ "fork1t",
+ "fork2b",
+ "fork2m",
+ "fork2t"
+ };
+
+ //Create the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() != NULL )
+ continue;
+
+ m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 64.0f );
+
+ // Different for different views
+ if ( ShouldDrawUsingViewModel() )
+ {
+ m_Parameters[i].SetAttachment( LookupAttachment( attachNamesGlow[i-PHYSCANNON_GLOW1] ) );
+ }
+ else
+ {
+ m_Parameters[i].SetAttachment( LookupAttachment( attachNamesGlowThirdPerson[i-PHYSCANNON_GLOW1] ) );
+ }
+ m_Parameters[i].SetColor( Vector( 255, 128, 0 ) );
+
+ if ( m_Parameters[i].SetMaterial( PHYSCANNON_GLOW_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+ // ------------------------------------------
+ // End caps
+ // ------------------------------------------
+
+ const char *attachNamesEndCap[NUM_ENDCAP_SPRITES] =
+ {
+ "fork1t",
+ "fork2t",
+ "fork3t"
+ };
+
+ //Create the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ if ( m_Parameters[i].GetMaterial() != NULL )
+ continue;
+
+ m_Parameters[i].GetScale().SetAbsolute( 0.05f * SPRITE_SCALE );
+ m_Parameters[i].GetAlpha().SetAbsolute( 255.0f );
+ m_Parameters[i].SetAttachment( LookupAttachment( attachNamesEndCap[i-PHYSCANNON_ENDCAP1] ) );
+ m_Parameters[i].SetVisible( false );
+
+ if ( m_Parameters[i].SetMaterial( PHYSCANNON_ENDCAP_SPRITE ) == false )
+ {
+ // This means the texture was not found
+ Assert( 0 );
+ }
+ }
+
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Closing effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectClosed( void )
+{
+
+#ifdef CLIENT_DLL
+
+ // Turn off the end-caps
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Ready effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectReady( void )
+{
+
+#ifdef CLIENT_DLL
+
+ // Special POV case
+ if ( ShouldDrawUsingViewModel() )
+ {
+ //Turn on the center sprite
+ m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 14.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 128.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].SetVisible();
+ }
+ else
+ {
+ //Turn off the center sprite
+ m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 8.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 0.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].SetVisible();
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.4f * SPRITE_SCALE, 0.2f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
+ m_Parameters[i].SetVisible();
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+#endif
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Holding effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectHolding( void )
+{
+
+#ifdef CLIENT_DLL
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ // Scale up the center sprite
+ m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 16.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 255.0f, 0.1f );
+ m_Parameters[PHYSCANNON_CORE].SetVisible();
+
+ // Prepare for scale up
+ m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
+ m_Parameters[i].SetVisible();
+ }
+
+ // Turn on the glow sprites
+ // NOTE: The last glow is left off for first-person
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES-1); i++ )
+ {
+ m_Parameters[i].SetVisible();
+ }
+
+ // Create our beams
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ CBaseEntity *pBeamEnt = pOwner->GetViewModel();
+
+ // Setup the beams
+ m_Beams[0].Init( LookupAttachment( "fork1t" ), 1, pBeamEnt, true );
+ m_Beams[1].Init( LookupAttachment( "fork2t" ), 1, pBeamEnt, true );
+
+ // Set them visible
+ m_Beams[0].SetVisible();
+ m_Beams[1].SetVisible();
+ }
+ else
+ {
+ // Scale up the center sprite
+ m_Parameters[PHYSCANNON_CORE].GetScale().InitFromCurrent( 14.0f, 0.2f );
+ m_Parameters[PHYSCANNON_CORE].GetAlpha().InitFromCurrent( 255.0f, 0.1f );
+ m_Parameters[PHYSCANNON_CORE].SetVisible();
+
+ // Prepare for scale up
+ m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].GetScale().InitFromCurrent( 0.5f * SPRITE_SCALE, 0.2f );
+ m_Parameters[i].GetAlpha().InitFromCurrent( 64.0f, 0.2f );
+ m_Parameters[i].SetVisible();
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible();
+ }
+
+ // Setup the beams
+ m_Beams[0].Init( LookupAttachment( "fork1t" ), 1, this, false );
+ m_Beams[1].Init( LookupAttachment( "fork2t" ), 1, this, false );
+ m_Beams[2].Init( LookupAttachment( "fork3t" ), 1, this, false );
+
+ // Set them visible
+ m_Beams[0].SetVisible();
+ m_Beams[1].SetVisible();
+ m_Beams[2].SetVisible();
+ }
+
+#endif
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Launch effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectLaunch( Vector *pos )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if ( pOwner == NULL )
+ return;
+
+ Vector endPos;
+ Vector shotDir;
+
+ // See if we need to predict this position
+ if ( pos == NULL )
+ {
+ // Hit an entity if we're holding one
+ if ( m_hAttachedObject )
+ {
+ endPos = m_hAttachedObject->WorldSpaceCenter();
+
+ shotDir = endPos - pOwner->Weapon_ShootPosition();
+ VectorNormalize( shotDir );
+ }
+ else
+ {
+ // Otherwise try and find the right spot
+ endPos = pOwner->Weapon_ShootPosition();
+ pOwner->EyeVectors( &shotDir );
+
+ trace_t tr;
+ UTIL_TraceLine( endPos, endPos + ( shotDir * MAX_TRACE_LENGTH ), MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ endPos = tr.endpos;
+ shotDir = endPos - pOwner->Weapon_ShootPosition();
+ VectorNormalize( shotDir );
+ }
+ }
+ else
+ {
+ // Use what is supplied
+ endPos = *pos;
+ shotDir = ( endPos - pOwner->Weapon_ShootPosition() );
+ VectorNormalize( shotDir );
+ }
+
+ // End hit
+ CPVSFilter filter( endPos );
+
+ // Don't send this to the owning player, they already had it predicted
+ if ( IsPredicted() )
+ {
+ filter.UsePredictionRules();
+ }
+
+ // Do an impact hit
+ CEffectData data;
+ data.m_vOrigin = endPos;
+#ifdef CLIENT_DLL
+ data.m_hEntity = GetRefEHandle();
+#else
+ data.m_nEntIndex = entindex();
+#endif
+
+ te->DispatchEffect( filter, 0.0, data.m_vOrigin, "PhyscannonImpact", data );
+
+#ifdef CLIENT_DLL
+
+ //Turn on the blast sprite and scale
+ m_Parameters[PHYSCANNON_BLAST].GetScale().Init( 8.0f, 64.0f, 0.1f );
+ m_Parameters[PHYSCANNON_BLAST].GetAlpha().Init( 255.0f, 0.0f, 0.2f );
+ m_Parameters[PHYSCANNON_BLAST].SetVisible();
+
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Shutdown for the weapon when it's holstered
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffectNone( void )
+{
+#ifdef CLIENT_DLL
+
+ //Turn off main glows
+ m_Parameters[PHYSCANNON_CORE].SetVisible( false );
+ m_Parameters[PHYSCANNON_BLAST].SetVisible( false );
+
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ // Turn on the glow sprites
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ m_Parameters[i].SetVisible( false );
+ }
+
+ m_Beams[0].SetVisible( false );
+ m_Beams[1].SetVisible( false );
+ m_Beams[2].SetVisible( false );
+
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : effectType -
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DoEffect( int effectType, Vector *pos )
+{
+ m_EffectState = effectType;
+
+#ifdef CLIENT_DLL
+ // Save predicted state
+ m_nOldEffectState = m_EffectState;
+#endif
+
+ switch( effectType )
+ {
+ case EFFECT_CLOSED:
+ DoEffectClosed( );
+ break;
+
+ case EFFECT_READY:
+ DoEffectReady( );
+ break;
+
+ case EFFECT_HOLDING:
+ DoEffectHolding();
+ break;
+
+ case EFFECT_LAUNCH:
+ DoEffectLaunch( pos );
+ break;
+
+ default:
+ case EFFECT_NONE:
+ DoEffectNone();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iIndex -
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CWeaponPhysCannon::GetShootSound( int iIndex ) const
+{
+ return BaseClass::GetShootSound( iIndex );
+}
+
+#ifdef CLIENT_DLL
+
+extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets the complete list of values needed to render an effect from an
+// effect parameter
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::GetEffectParameters( EffectType_t effectID, color32 &color, float &scale, IMaterial **pMaterial, Vector &vecAttachment )
+{
+ const float dt = gpGlobals->curtime;
+
+ // Get alpha
+ float alpha = m_Parameters[effectID].GetAlpha().Interp( dt );
+
+ // Get scale
+ scale = m_Parameters[effectID].GetScale().Interp( dt );
+
+ // Get material
+ *pMaterial = (IMaterial *) m_Parameters[effectID].GetMaterial();
+
+ // Setup the color
+ color.r = (int) m_Parameters[effectID].GetColor().x;
+ color.g = (int) m_Parameters[effectID].GetColor().y;
+ color.b = (int) m_Parameters[effectID].GetColor().z;
+ color.a = (int) alpha;
+
+ // Setup the attachment
+ int attachment = m_Parameters[effectID].GetAttachment();
+ QAngle angles;
+
+ // Format for first-person
+ if ( ShouldDrawUsingViewModel() )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner != NULL )
+ {
+ pOwner->GetViewModel()->GetAttachment( attachment, vecAttachment, angles );
+ ::FormatViewModelAttachment( vecAttachment, true );
+ }
+ }
+ else
+ {
+ GetAttachment( attachment, vecAttachment, angles );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Whether or not an effect is set to display
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::IsEffectVisible( EffectType_t effectID )
+{
+ return m_Parameters[effectID].IsVisible();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the effect sprite, given an effect parameter ID
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DrawEffectSprite( EffectType_t effectID )
+{
+ color32 color;
+ float scale;
+ IMaterial *pMaterial;
+ Vector vecAttachment;
+
+ // Don't draw invisible effects
+ if ( IsEffectVisible( effectID ) == false )
+ return;
+
+ // Get all of our parameters
+ GetEffectParameters( effectID, color, scale, &pMaterial, vecAttachment );
+
+ // Msg( "Scale: %.2f\tAlpha: %.2f\n", scale, alpha );
+
+ // Don't render fully translucent objects
+ if ( color.a <= 0.0f )
+ return;
+
+ // Draw the sprite
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( pMaterial, this );
+ DrawSprite( vecAttachment, scale, scale, color );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Render our third-person effects
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::DrawEffects( void )
+{
+ // Draw the core effects
+ DrawEffectSprite( PHYSCANNON_CORE );
+ DrawEffectSprite( PHYSCANNON_BLAST );
+
+ // Draw the glows
+ for ( int i = PHYSCANNON_GLOW1; i < (PHYSCANNON_GLOW1+NUM_GLOW_SPRITES); i++ )
+ {
+ DrawEffectSprite( (EffectType_t) i );
+ }
+
+ // Draw the endcaps
+ for ( int i = PHYSCANNON_ENDCAP1; i < (PHYSCANNON_ENDCAP1+NUM_ENDCAP_SPRITES); i++ )
+ {
+ DrawEffectSprite( (EffectType_t) i );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Third-person function call to render world model
+//-----------------------------------------------------------------------------
+int CWeaponPhysCannon::DrawModel( int flags )
+{
+ // Only render these on the transparent pass
+ if ( flags & STUDIO_TRANSPARENCY )
+ {
+ DrawEffects();
+ return 1;
+ }
+
+ // Only do this on the opaque pass
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: First-person function call after viewmodel has been drawn
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
+{
+ // Render our effects
+ DrawEffects();
+
+ // Pass this back up
+ BaseClass::ViewModelDrawn( pBaseViewModel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We are always considered transparent
+//-----------------------------------------------------------------------------
+bool CWeaponPhysCannon::IsTransparent( void )
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPhysCannon::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ BaseClass::NotifyShouldTransmit(state);
+
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ DoEffect( EFFECT_NONE );
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// EXTERNAL API
+//-----------------------------------------------------------------------------
+void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis )
+{
+ CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
+ if ( pCannon )
+ {
+ if ( pOnlyIfHoldingThis )
+ {
+ pCannon->DropIfEntityHeld( pOnlyIfHoldingThis );
+ }
+ else
+ {
+ pCannon->ForceDrop();
+ }
+ }
+}
+
+bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupControllerEntity, CBaseEntity *pHeldEntity )
+{
+ CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
+
+ return pController ? pController->IsHoldingEntity( pHeldEntity ) : false;
+}
+
+
+float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject )
+{
+ float mass = 0.0f;
+ CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
+ if ( pCannon )
+ {
+ CGrabController &grab = pCannon->GetGrabController();
+ mass = grab.GetSavedMass( pHeldObject );
+ }
+
+ return mass;
+}
+
+CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon )
+{
+ CWeaponPhysCannon *pCannon = dynamic_cast<CWeaponPhysCannon *>(pActiveWeapon);
+ if ( pCannon )
+ {
+ CGrabController &grab = pCannon->GetGrabController();
+ return grab.GetAttached();
+ }
+
+ return NULL;
+}
+
+float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject )
+{
+ float mass = 0.0f;
+ CPlayerPickupController *pController = dynamic_cast<CPlayerPickupController *>(pPickupControllerEntity);
+ if ( pController )
+ {
+ CGrabController &grab = pController->GetGrabController();
+ mass = grab.GetSavedMass( pHeldObject );
+ }
+ return mass;
+}
+
+#ifdef CLIENT_DLL
+
+extern void FX_GaussExplosion( const Vector &pos, const Vector &dir, int type );
+
+void CallbackPhyscannonImpact( const CEffectData &data )
+{
+ C_BaseEntity *pEnt = data.GetEntity();
+ if ( pEnt == NULL )
+ return;
+
+ Vector vecAttachment;
+ QAngle vecAngles;
+
+ C_BaseCombatWeapon *pWeapon = dynamic_cast<C_BaseCombatWeapon *>(pEnt);
+
+ if ( pWeapon == NULL )
+ return;
+
+ pWeapon->GetAttachment( 1, vecAttachment, vecAngles );
+
+ Vector dir = ( data.m_vOrigin - vecAttachment );
+ VectorNormalize( dir );
+
+ // Do special first-person fix-up
+ if ( pWeapon->GetOwner() == CBasePlayer::GetLocalPlayer() )
+ {
+ // Translate the attachment entity to the viewmodel
+ C_BasePlayer *pPlayer = dynamic_cast<C_BasePlayer *>(pWeapon->GetOwner());
+
+ if ( pPlayer )
+ {
+ pEnt = pPlayer->GetViewModel();
+ }
+
+ // Format attachment for first-person view!
+ ::FormatViewModelAttachment( vecAttachment, true );
+
+ // Explosions at the impact point
+ FX_GaussExplosion( data.m_vOrigin, -dir, 0 );
+
+ // Draw a beam
+ BeamInfo_t beamInfo;
+
+ beamInfo.m_pStartEnt = pEnt;
+ beamInfo.m_nStartAttachment = 1;
+ beamInfo.m_pEndEnt = NULL;
+ beamInfo.m_nEndAttachment = -1;
+ beamInfo.m_vecStart = vec3_origin;
+ beamInfo.m_vecEnd = data.m_vOrigin;
+ beamInfo.m_pszModelName = PHYSCANNON_BEAM_SPRITE;
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.1f;
+ beamInfo.m_flWidth = 12.0f;
+ beamInfo.m_flEndWidth = 4.0f;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 0;
+ beamInfo.m_flBrightness = 255.0;
+ beamInfo.m_flSpeed = 0.0f;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 30.0;
+ beamInfo.m_flRed = 255.0;
+ beamInfo.m_flGreen = 255.0;
+ beamInfo.m_flBlue = 255.0;
+ beamInfo.m_nSegments = 16;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = FBEAM_ONLYNOISEONCE;
+
+ beams->CreateBeamEntPoint( beamInfo );
+ }
+ else
+ {
+ // Explosion at the starting point
+ FX_GaussExplosion( vecAttachment, dir, 0 );
+ }
+}
+
+DECLARE_CLIENT_EFFECT( "PhyscannonImpact", CallbackPhyscannonImpact );
+
+#endif
diff --git a/mp/src/game/shared/hl2mp/weapon_physcannon.h b/mp/src/game/shared/hl2mp/weapon_physcannon.h
index 8e2cc4f1..03f60f80 100644
--- a/mp/src/game/shared/hl2mp/weapon_physcannon.h
+++ b/mp/src/game/shared/hl2mp/weapon_physcannon.h
@@ -1,30 +1,30 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#ifndef WEAPON_PHYSCANNON_H
-#define WEAPON_PHYSCANNON_H
-#ifdef _WIN32
-#pragma once
-#endif
-
-
-
-//-----------------------------------------------------------------------------
-// Do we have the super-phys gun?
-//-----------------------------------------------------------------------------
-bool PlayerHasMegaPhysCannon();
-
-// force the physcannon to drop an object (if carried)
-void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis );
-void PhysCannonBeginUpgrade( CBaseAnimating *pAnim );
-
-bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupController, CBaseEntity *pHeldEntity );
-float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject );
-float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject );
-
-CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon );
-
-#endif // WEAPON_PHYSCANNON_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_PHYSCANNON_H
+#define WEAPON_PHYSCANNON_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+
+//-----------------------------------------------------------------------------
+// Do we have the super-phys gun?
+//-----------------------------------------------------------------------------
+bool PlayerHasMegaPhysCannon();
+
+// force the physcannon to drop an object (if carried)
+void PhysCannonForceDrop( CBaseCombatWeapon *pActiveWeapon, CBaseEntity *pOnlyIfHoldingThis );
+void PhysCannonBeginUpgrade( CBaseAnimating *pAnim );
+
+bool PlayerPickupControllerIsHoldingEntity( CBaseEntity *pPickupController, CBaseEntity *pHeldEntity );
+float PlayerPickupGetHeldObjectMass( CBaseEntity *pPickupControllerEntity, IPhysicsObject *pHeldObject );
+float PhysCannonGetHeldObjectMass( CBaseCombatWeapon *pActiveWeapon, IPhysicsObject *pHeldObject );
+
+CBaseEntity *PhysCannonGetHeldEntity( CBaseCombatWeapon *pActiveWeapon );
+
+#endif // WEAPON_PHYSCANNON_H
diff --git a/mp/src/game/shared/hl2mp/weapon_pistol.cpp b/mp/src/game/shared/hl2mp/weapon_pistol.cpp
index 83edb76b..5a9533c4 100644
--- a/mp/src/game/shared/hl2mp/weapon_pistol.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_pistol.cpp
@@ -1,339 +1,339 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "in_buttons.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
-#else
- #include "hl2mp_player.h"
-#endif
-
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-
-#define PISTOL_FASTEST_REFIRE_TIME 0.1f
-#define PISTOL_FASTEST_DRY_REFIRE_TIME 0.2f
-
-#define PISTOL_ACCURACY_SHOT_PENALTY_TIME 0.2f // Applied amount of time each shot adds to the time we must recover from
-#define PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME 1.5f // Maximum penalty to deal out
-
-#ifdef CLIENT_DLL
-#define CWeaponPistol C_WeaponPistol
-#endif
-
-//-----------------------------------------------------------------------------
-// CWeaponPistol
-//-----------------------------------------------------------------------------
-
-class CWeaponPistol : public CBaseHL2MPCombatWeapon
-{
-public:
- DECLARE_CLASS( CWeaponPistol, CBaseHL2MPCombatWeapon );
-
- CWeaponPistol(void);
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- void Precache( void );
- void ItemPostFrame( void );
- void ItemPreFrame( void );
- void ItemBusyFrame( void );
- void PrimaryAttack( void );
- void AddViewKick( void );
- void DryFire( void );
-
- void UpdatePenaltyTime( void );
-
- Activity GetPrimaryAttackActivity( void );
-
- virtual bool Reload( void );
-
- virtual const Vector& GetBulletSpread( void )
- {
- static Vector cone;
-
- float ramp = RemapValClamped( m_flAccuracyPenalty,
- 0.0f,
- PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME,
- 0.0f,
- 1.0f );
-
- // We lerp from very accurate to inaccurate over time
- VectorLerp( VECTOR_CONE_1DEGREES, VECTOR_CONE_6DEGREES, ramp, cone );
-
- return cone;
- }
-
- virtual int GetMinBurst()
- {
- return 1;
- }
-
- virtual int GetMaxBurst()
- {
- return 3;
- }
-
- virtual float GetFireRate( void )
- {
- return 0.5f;
- }
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-
-private:
- CNetworkVar( float, m_flSoonestPrimaryAttack );
- CNetworkVar( float, m_flLastAttackTime );
- CNetworkVar( float, m_flAccuracyPenalty );
- CNetworkVar( int, m_nNumShotsFired );
-
-private:
- CWeaponPistol( const CWeaponPistol & );
-};
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPistol, DT_WeaponPistol )
-
-BEGIN_NETWORK_TABLE( CWeaponPistol, DT_WeaponPistol )
-#ifdef CLIENT_DLL
- RecvPropTime( RECVINFO( m_flSoonestPrimaryAttack ) ),
- RecvPropTime( RECVINFO( m_flLastAttackTime ) ),
- RecvPropFloat( RECVINFO( m_flAccuracyPenalty ) ),
- RecvPropInt( RECVINFO( m_nNumShotsFired ) ),
-#else
- SendPropTime( SENDINFO( m_flSoonestPrimaryAttack ) ),
- SendPropTime( SENDINFO( m_flLastAttackTime ) ),
- SendPropFloat( SENDINFO( m_flAccuracyPenalty ) ),
- SendPropInt( SENDINFO( m_nNumShotsFired ) ),
-#endif
-END_NETWORK_TABLE()
-
-#ifdef CLIENT_DLL
-BEGIN_PREDICTION_DATA( CWeaponPistol )
- DEFINE_PRED_FIELD( m_flSoonestPrimaryAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_flLastAttackTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_flAccuracyPenalty, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_nNumShotsFired, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
-END_PREDICTION_DATA()
-#endif
-
-LINK_ENTITY_TO_CLASS( weapon_pistol, CWeaponPistol );
-PRECACHE_WEAPON_REGISTER( weapon_pistol );
-
-#ifndef CLIENT_DLL
-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 },
-};
-
-
-IMPLEMENT_ACTTABLE( CWeaponPistol );
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-//-----------------------------------------------------------------------------
-CWeaponPistol::CWeaponPistol( void )
-{
- m_flSoonestPrimaryAttack = gpGlobals->curtime;
- m_flAccuracyPenalty = 0.0f;
-
- m_fMinRange1 = 24;
- m_fMaxRange1 = 1500;
- m_fMinRange2 = 24;
- m_fMaxRange2 = 200;
-
- m_bFiresUnderwater = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPistol::Precache( void )
-{
- BaseClass::Precache();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPistol::DryFire( void )
-{
- WeaponSound( EMPTY );
- SendWeaponAnim( ACT_VM_DRYFIRE );
-
- m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_DRY_REFIRE_TIME;
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPistol::PrimaryAttack( void )
-{
- if ( ( gpGlobals->curtime - m_flLastAttackTime ) > 0.5f )
- {
- m_nNumShotsFired = 0;
- }
- else
- {
- m_nNumShotsFired++;
- }
-
- m_flLastAttackTime = gpGlobals->curtime;
- m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if( pOwner )
- {
- // Each time the player fires the pistol, reset the view punch. This prevents
- // the aim from 'drifting off' when the player fires very quickly. This may
- // not be the ideal way to achieve this, but it's cheap and it works, which is
- // great for a feature we're evaluating. (sjb)
- pOwner->ViewPunchReset();
- }
-
- BaseClass::PrimaryAttack();
-
- // Add an accuracy penalty which can move past our maximum penalty time if we're really spastic
- m_flAccuracyPenalty += PISTOL_ACCURACY_SHOT_PENALTY_TIME;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPistol::UpdatePenaltyTime( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- // Check our penalty time decay
- if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) && ( m_flSoonestPrimaryAttack < gpGlobals->curtime ) )
- {
- m_flAccuracyPenalty -= gpGlobals->frametime;
- m_flAccuracyPenalty = clamp( m_flAccuracyPenalty, 0.0f, PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPistol::ItemPreFrame( void )
-{
- UpdatePenaltyTime();
-
- BaseClass::ItemPreFrame();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPistol::ItemBusyFrame( void )
-{
- UpdatePenaltyTime();
-
- BaseClass::ItemBusyFrame();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Allows firing as fast as button is pressed
-//-----------------------------------------------------------------------------
-void CWeaponPistol::ItemPostFrame( void )
-{
- BaseClass::ItemPostFrame();
-
- if ( m_bInReload )
- return;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- if ( pOwner->m_nButtons & IN_ATTACK2 )
- {
- m_flLastAttackTime = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
- m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
- m_flNextPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
- }
-
- //Allow a refire as fast as the player can click
- if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) && ( m_flSoonestPrimaryAttack < gpGlobals->curtime ) )
- {
- m_flNextPrimaryAttack = gpGlobals->curtime - 0.1f;
- }
- else if ( ( pOwner->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack < gpGlobals->curtime ) && ( m_iClip1 <= 0 ) )
- {
- DryFire();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : int
-//-----------------------------------------------------------------------------
-Activity CWeaponPistol::GetPrimaryAttackActivity( void )
-{
- if ( m_nNumShotsFired < 1 )
- return ACT_VM_PRIMARYATTACK;
-
- if ( m_nNumShotsFired < 2 )
- return ACT_VM_RECOIL1;
-
- if ( m_nNumShotsFired < 3 )
- return ACT_VM_RECOIL2;
-
- return ACT_VM_RECOIL3;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CWeaponPistol::Reload( void )
-{
- bool fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
- if ( fRet )
- {
- WeaponSound( RELOAD );
- m_flAccuracyPenalty = 0.0f;
- }
- return fRet;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponPistol::AddViewKick( void )
-{
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if ( pPlayer == NULL )
- return;
-
- QAngle viewPunch;
-
- viewPunch.x = SharedRandomFloat( "pistolpax", 0.25f, 0.5f );
- viewPunch.y = SharedRandomFloat( "pistolpay", -.6f, .6f );
- viewPunch.z = 0.0f;
-
- //Add it to the view punch
- pPlayer->ViewPunch( viewPunch );
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+#else
+ #include "hl2mp_player.h"
+#endif
+
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+
+#define PISTOL_FASTEST_REFIRE_TIME 0.1f
+#define PISTOL_FASTEST_DRY_REFIRE_TIME 0.2f
+
+#define PISTOL_ACCURACY_SHOT_PENALTY_TIME 0.2f // Applied amount of time each shot adds to the time we must recover from
+#define PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME 1.5f // Maximum penalty to deal out
+
+#ifdef CLIENT_DLL
+#define CWeaponPistol C_WeaponPistol
+#endif
+
+//-----------------------------------------------------------------------------
+// CWeaponPistol
+//-----------------------------------------------------------------------------
+
+class CWeaponPistol : public CBaseHL2MPCombatWeapon
+{
+public:
+ DECLARE_CLASS( CWeaponPistol, CBaseHL2MPCombatWeapon );
+
+ CWeaponPistol(void);
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ void Precache( void );
+ void ItemPostFrame( void );
+ void ItemPreFrame( void );
+ void ItemBusyFrame( void );
+ void PrimaryAttack( void );
+ void AddViewKick( void );
+ void DryFire( void );
+
+ void UpdatePenaltyTime( void );
+
+ Activity GetPrimaryAttackActivity( void );
+
+ virtual bool Reload( void );
+
+ virtual const Vector& GetBulletSpread( void )
+ {
+ static Vector cone;
+
+ float ramp = RemapValClamped( m_flAccuracyPenalty,
+ 0.0f,
+ PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME,
+ 0.0f,
+ 1.0f );
+
+ // We lerp from very accurate to inaccurate over time
+ VectorLerp( VECTOR_CONE_1DEGREES, VECTOR_CONE_6DEGREES, ramp, cone );
+
+ return cone;
+ }
+
+ virtual int GetMinBurst()
+ {
+ return 1;
+ }
+
+ virtual int GetMaxBurst()
+ {
+ return 3;
+ }
+
+ virtual float GetFireRate( void )
+ {
+ return 0.5f;
+ }
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+
+private:
+ CNetworkVar( float, m_flSoonestPrimaryAttack );
+ CNetworkVar( float, m_flLastAttackTime );
+ CNetworkVar( float, m_flAccuracyPenalty );
+ CNetworkVar( int, m_nNumShotsFired );
+
+private:
+ CWeaponPistol( const CWeaponPistol & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponPistol, DT_WeaponPistol )
+
+BEGIN_NETWORK_TABLE( CWeaponPistol, DT_WeaponPistol )
+#ifdef CLIENT_DLL
+ RecvPropTime( RECVINFO( m_flSoonestPrimaryAttack ) ),
+ RecvPropTime( RECVINFO( m_flLastAttackTime ) ),
+ RecvPropFloat( RECVINFO( m_flAccuracyPenalty ) ),
+ RecvPropInt( RECVINFO( m_nNumShotsFired ) ),
+#else
+ SendPropTime( SENDINFO( m_flSoonestPrimaryAttack ) ),
+ SendPropTime( SENDINFO( m_flLastAttackTime ) ),
+ SendPropFloat( SENDINFO( m_flAccuracyPenalty ) ),
+ SendPropInt( SENDINFO( m_nNumShotsFired ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponPistol )
+ DEFINE_PRED_FIELD( m_flSoonestPrimaryAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flLastAttackTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flAccuracyPenalty, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_nNumShotsFired, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_pistol, CWeaponPistol );
+PRECACHE_WEAPON_REGISTER( weapon_pistol );
+
+#ifndef CLIENT_DLL
+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 },
+};
+
+
+IMPLEMENT_ACTTABLE( CWeaponPistol );
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CWeaponPistol::CWeaponPistol( void )
+{
+ m_flSoonestPrimaryAttack = gpGlobals->curtime;
+ m_flAccuracyPenalty = 0.0f;
+
+ m_fMinRange1 = 24;
+ m_fMaxRange1 = 1500;
+ m_fMinRange2 = 24;
+ m_fMaxRange2 = 200;
+
+ m_bFiresUnderwater = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistol::Precache( void )
+{
+ BaseClass::Precache();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistol::DryFire( void )
+{
+ WeaponSound( EMPTY );
+ SendWeaponAnim( ACT_VM_DRYFIRE );
+
+ m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_DRY_REFIRE_TIME;
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistol::PrimaryAttack( void )
+{
+ if ( ( gpGlobals->curtime - m_flLastAttackTime ) > 0.5f )
+ {
+ m_nNumShotsFired = 0;
+ }
+ else
+ {
+ m_nNumShotsFired++;
+ }
+
+ m_flLastAttackTime = gpGlobals->curtime;
+ m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if( pOwner )
+ {
+ // Each time the player fires the pistol, reset the view punch. This prevents
+ // the aim from 'drifting off' when the player fires very quickly. This may
+ // not be the ideal way to achieve this, but it's cheap and it works, which is
+ // great for a feature we're evaluating. (sjb)
+ pOwner->ViewPunchReset();
+ }
+
+ BaseClass::PrimaryAttack();
+
+ // Add an accuracy penalty which can move past our maximum penalty time if we're really spastic
+ m_flAccuracyPenalty += PISTOL_ACCURACY_SHOT_PENALTY_TIME;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistol::UpdatePenaltyTime( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ // Check our penalty time decay
+ if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) && ( m_flSoonestPrimaryAttack < gpGlobals->curtime ) )
+ {
+ m_flAccuracyPenalty -= gpGlobals->frametime;
+ m_flAccuracyPenalty = clamp( m_flAccuracyPenalty, 0.0f, PISTOL_ACCURACY_MAXIMUM_PENALTY_TIME );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistol::ItemPreFrame( void )
+{
+ UpdatePenaltyTime();
+
+ BaseClass::ItemPreFrame();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistol::ItemBusyFrame( void )
+{
+ UpdatePenaltyTime();
+
+ BaseClass::ItemBusyFrame();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allows firing as fast as button is pressed
+//-----------------------------------------------------------------------------
+void CWeaponPistol::ItemPostFrame( void )
+{
+ BaseClass::ItemPostFrame();
+
+ if ( m_bInReload )
+ return;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( pOwner->m_nButtons & IN_ATTACK2 )
+ {
+ m_flLastAttackTime = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
+ m_flSoonestPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
+ m_flNextPrimaryAttack = gpGlobals->curtime + PISTOL_FASTEST_REFIRE_TIME;
+ }
+
+ //Allow a refire as fast as the player can click
+ if ( ( ( pOwner->m_nButtons & IN_ATTACK ) == false ) && ( m_flSoonestPrimaryAttack < gpGlobals->curtime ) )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime - 0.1f;
+ }
+ else if ( ( pOwner->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack < gpGlobals->curtime ) && ( m_iClip1 <= 0 ) )
+ {
+ DryFire();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+Activity CWeaponPistol::GetPrimaryAttackActivity( void )
+{
+ if ( m_nNumShotsFired < 1 )
+ return ACT_VM_PRIMARYATTACK;
+
+ if ( m_nNumShotsFired < 2 )
+ return ACT_VM_RECOIL1;
+
+ if ( m_nNumShotsFired < 3 )
+ return ACT_VM_RECOIL2;
+
+ return ACT_VM_RECOIL3;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CWeaponPistol::Reload( void )
+{
+ bool fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
+ if ( fRet )
+ {
+ WeaponSound( RELOAD );
+ m_flAccuracyPenalty = 0.0f;
+ }
+ return fRet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistol::AddViewKick( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if ( pPlayer == NULL )
+ return;
+
+ QAngle viewPunch;
+
+ viewPunch.x = SharedRandomFloat( "pistolpax", 0.25f, 0.5f );
+ viewPunch.y = SharedRandomFloat( "pistolpay", -.6f, .6f );
+ viewPunch.z = 0.0f;
+
+ //Add it to the view punch
+ pPlayer->ViewPunch( viewPunch );
+}
diff --git a/mp/src/game/shared/hl2mp/weapon_rpg.cpp b/mp/src/game/shared/hl2mp/weapon_rpg.cpp
index 89af513d..aa3cc8fd 100644
--- a/mp/src/game/shared/hl2mp/weapon_rpg.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_rpg.cpp
@@ -1,2283 +1,2283 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "in_buttons.h"
-#include "weapon_rpg.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
- #include "model_types.h"
- #include "beamdraw.h"
- #include "fx_line.h"
- #include "view.h"
-#else
- #include "basecombatcharacter.h"
- #include "movie_explosion.h"
- #include "soundent.h"
- #include "player.h"
- #include "rope.h"
- #include "vstdlib/random.h"
- #include "engine/IEngineSound.h"
- #include "explode.h"
- #include "util.h"
- #include "in_buttons.h"
- #include "shake.h"
- #include "te_effect_dispatch.h"
- #include "triggers.h"
- #include "smoke_trail.h"
- #include "collisionutils.h"
- #include "hl2_shareddefs.h"
-#endif
-
-#include "debugoverlay_shared.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define RPG_SPEED 1500
-
-#ifndef CLIENT_DLL
-const char *g_pLaserDotThink = "LaserThinkContext";
-
-static ConVar sk_apc_missile_damage("sk_apc_missile_damage", "15");
-#define APC_MISSILE_DAMAGE sk_apc_missile_damage.GetFloat()
-
-#endif
-
-#ifdef CLIENT_DLL
-#define CLaserDot C_LaserDot
-#endif
-
-//-----------------------------------------------------------------------------
-// Laser Dot
-//-----------------------------------------------------------------------------
-class CLaserDot : public CBaseEntity
-{
- DECLARE_CLASS( CLaserDot, CBaseEntity );
-public:
-
- CLaserDot( void );
- ~CLaserDot( void );
-
- static CLaserDot *Create( const Vector &origin, CBaseEntity *pOwner = NULL, bool bVisibleDot = true );
-
- void SetTargetEntity( CBaseEntity *pTarget ) { m_hTargetEnt = pTarget; }
- CBaseEntity *GetTargetEntity( void ) { return m_hTargetEnt; }
-
- void SetLaserPosition( const Vector &origin, const Vector &normal );
- Vector GetChasePosition();
- void TurnOn( void );
- void TurnOff( void );
- bool IsOn() const { return m_bIsOn; }
-
- void Toggle( void );
-
- int ObjectCaps() { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
-
- void MakeInvisible( void );
-
-#ifdef CLIENT_DLL
-
- virtual bool IsTransparent( void ) { return true; }
- virtual RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TRANSLUCENT_ENTITY; }
- virtual int DrawModel( int flags );
- virtual void OnDataChanged( DataUpdateType_t updateType );
- virtual bool ShouldDraw( void ) { return (IsEffectActive(EF_NODRAW)==false); }
-
- CMaterialReference m_hSpriteMaterial;
-#endif
-
-protected:
- Vector m_vecSurfaceNormal;
- EHANDLE m_hTargetEnt;
- bool m_bVisibleLaserDot;
- bool m_bIsOn;
-
- DECLARE_NETWORKCLASS();
- DECLARE_DATADESC();
-public:
- CLaserDot *m_pNext;
-};
-
-IMPLEMENT_NETWORKCLASS_ALIASED( LaserDot, DT_LaserDot )
-
-BEGIN_NETWORK_TABLE( CLaserDot, DT_LaserDot )
-END_NETWORK_TABLE()
-
-#ifndef CLIENT_DLL
-
-// a list of laser dots to search quickly
-CEntityClassList<CLaserDot> g_LaserDotList;
-template <> CLaserDot *CEntityClassList<CLaserDot>::m_pClassList = NULL;
-CLaserDot *GetLaserDotList()
-{
- return g_LaserDotList.m_pClassList;
-}
-
-BEGIN_DATADESC( CMissile )
-
- DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
- DEFINE_FIELD( m_hRocketTrail, FIELD_EHANDLE ),
- DEFINE_FIELD( m_flAugerTime, FIELD_TIME ),
- DEFINE_FIELD( m_flMarkDeadTime, FIELD_TIME ),
- DEFINE_FIELD( m_flGracePeriodEndsAt, FIELD_TIME ),
- DEFINE_FIELD( m_flDamage, FIELD_FLOAT ),
-
- // Function Pointers
- DEFINE_FUNCTION( MissileTouch ),
- DEFINE_FUNCTION( AccelerateThink ),
- DEFINE_FUNCTION( AugerThink ),
- DEFINE_FUNCTION( IgniteThink ),
- DEFINE_FUNCTION( SeekThink ),
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( rpg_missile, CMissile );
-
-class CWeaponRPG;
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CMissile::CMissile()
-{
- m_hRocketTrail = NULL;
-}
-
-CMissile::~CMissile()
-{
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CMissile::Precache( void )
-{
- PrecacheModel( "models/weapons/w_missile.mdl" );
- PrecacheModel( "models/weapons/w_missile_launch.mdl" );
- PrecacheModel( "models/weapons/w_missile_closed.mdl" );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CMissile::Spawn( void )
-{
- Precache();
-
- SetSolid( SOLID_BBOX );
- SetModel("models/weapons/w_missile_launch.mdl");
- UTIL_SetSize( this, -Vector(4,4,4), Vector(4,4,4) );
-
- SetTouch( &CMissile::MissileTouch );
-
- SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
- SetThink( &CMissile::IgniteThink );
-
- SetNextThink( gpGlobals->curtime + 0.3f );
-
- m_takedamage = DAMAGE_YES;
- m_iHealth = m_iMaxHealth = 100;
- m_bloodColor = DONT_BLEED;
- m_flGracePeriodEndsAt = 0;
-
- AddFlag( FL_OBJECT );
-}
-
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CMissile::Event_Killed( const CTakeDamageInfo &info )
-{
- m_takedamage = DAMAGE_NO;
-
- ShotDown();
-}
-
-unsigned int CMissile::PhysicsSolidMaskForEntity( void ) const
-{
- return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX;
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-int CMissile::OnTakeDamage_Alive( const CTakeDamageInfo &info )
-{
- if ( ( info.GetDamageType() & (DMG_MISSILEDEFENSE | DMG_AIRBOAT) ) == false )
- return 0;
-
- bool bIsDamaged;
- if( m_iHealth <= AugerHealth() )
- {
- // This missile is already damaged (i.e., already running AugerThink)
- bIsDamaged = true;
- }
- else
- {
- // This missile isn't damaged enough to wobble in flight yet
- bIsDamaged = false;
- }
-
- int nRetVal = BaseClass::OnTakeDamage_Alive( info );
-
- if( !bIsDamaged )
- {
- if ( m_iHealth <= AugerHealth() )
- {
- ShotDown();
- }
- }
-
- return nRetVal;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Stops any kind of tracking and shoots dumb
-//-----------------------------------------------------------------------------
-void CMissile::DumbFire( void )
-{
- SetThink( NULL );
- SetMoveType( MOVETYPE_FLY );
-
- SetModel("models/weapons/w_missile.mdl");
- UTIL_SetSize( this, vec3_origin, vec3_origin );
-
- EmitSound( "Missile.Ignite" );
-
- // Smoke trail.
- CreateSmokeTrail();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMissile::SetGracePeriod( float flGracePeriod )
-{
- m_flGracePeriodEndsAt = gpGlobals->curtime + flGracePeriod;
-
- // Go non-solid until the grace period ends
- AddSolidFlags( FSOLID_NOT_SOLID );
-}
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CMissile::AccelerateThink( void )
-{
- Vector vecForward;
-
- // !!!UNDONE - make this work exactly the same as HL1 RPG, lest we have looping sound bugs again!
- EmitSound( "Missile.Accelerate" );
-
- // SetEffects( EF_LIGHT );
-
- AngleVectors( GetLocalAngles(), &vecForward );
- SetAbsVelocity( vecForward * RPG_SPEED );
-
- SetThink( &CMissile::SeekThink );
- SetNextThink( gpGlobals->curtime + 0.1f );
-}
-
-#define AUGER_YDEVIANCE 20.0f
-#define AUGER_XDEVIANCEUP 8.0f
-#define AUGER_XDEVIANCEDOWN 1.0f
-
-//---------------------------------------------------------
-//---------------------------------------------------------
-void CMissile::AugerThink( void )
-{
- // If we've augered long enough, then just explode
- if ( m_flAugerTime < gpGlobals->curtime )
- {
- Explode();
- return;
- }
-
- if ( m_flMarkDeadTime < gpGlobals->curtime )
- {
- m_lifeState = LIFE_DYING;
- }
-
- QAngle angles = GetLocalAngles();
-
- angles.y += random->RandomFloat( -AUGER_YDEVIANCE, AUGER_YDEVIANCE );
- angles.x += random->RandomFloat( -AUGER_XDEVIANCEDOWN, AUGER_XDEVIANCEUP );
-
- SetLocalAngles( angles );
-
- Vector vecForward;
-
- AngleVectors( GetLocalAngles(), &vecForward );
-
- SetAbsVelocity( vecForward * 1000.0f );
-
- SetNextThink( gpGlobals->curtime + 0.05f );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Causes the missile to spiral to the ground and explode, due to damage
-//-----------------------------------------------------------------------------
-void CMissile::ShotDown( void )
-{
- CEffectData data;
- data.m_vOrigin = GetAbsOrigin();
-
- DispatchEffect( "RPGShotDown", data );
-
- if ( m_hRocketTrail != NULL )
- {
- m_hRocketTrail->m_bDamaged = true;
- }
-
- SetThink( &CMissile::AugerThink );
- SetNextThink( gpGlobals->curtime );
- m_flAugerTime = gpGlobals->curtime + 1.5f;
- m_flMarkDeadTime = gpGlobals->curtime + 0.75;
-
- // Let the RPG start reloading immediately
- if ( m_hOwner != NULL )
- {
- m_hOwner->NotifyRocketDied();
- m_hOwner = NULL;
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// The actual explosion
-//-----------------------------------------------------------------------------
-void CMissile::DoExplosion( void )
-{
- // Explode
- ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), GetDamage(), GetDamage() * 2,
- SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this);
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMissile::Explode( void )
-{
- // Don't explode against the skybox. Just pretend that
- // the missile flies off into the distance.
- Vector forward;
-
- GetVectors( &forward, NULL, NULL );
-
- trace_t tr;
- UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + forward * 16, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
-
- m_takedamage = DAMAGE_NO;
- SetSolid( SOLID_NONE );
- if( tr.fraction == 1.0 || !(tr.surface.flags & SURF_SKY) )
- {
- DoExplosion();
- }
-
- if( m_hRocketTrail )
- {
- m_hRocketTrail->SetLifetime(0.1f);
- m_hRocketTrail = NULL;
- }
-
- if ( m_hOwner != NULL )
- {
- m_hOwner->NotifyRocketDied();
- m_hOwner = NULL;
- }
-
- StopSound( "Missile.Ignite" );
- UTIL_Remove( this );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pOther -
-//-----------------------------------------------------------------------------
-void CMissile::MissileTouch( CBaseEntity *pOther )
-{
- Assert( pOther );
-
- // Don't touch triggers (but DO hit weapons)
- if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON )
- return;
-
- Explode();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMissile::CreateSmokeTrail( void )
-{
- if ( m_hRocketTrail )
- return;
-
- // Smoke trail.
- if ( (m_hRocketTrail = RocketTrail::CreateRocketTrail()) != NULL )
- {
- m_hRocketTrail->m_Opacity = 0.2f;
- m_hRocketTrail->m_SpawnRate = 100;
- m_hRocketTrail->m_ParticleLifetime = 0.5f;
- m_hRocketTrail->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
- m_hRocketTrail->m_EndColor.Init( 0.0, 0.0, 0.0 );
- m_hRocketTrail->m_StartSize = 8;
- m_hRocketTrail->m_EndSize = 32;
- m_hRocketTrail->m_SpawnRadius = 4;
- m_hRocketTrail->m_MinSpeed = 2;
- m_hRocketTrail->m_MaxSpeed = 16;
-
- m_hRocketTrail->SetLifetime( 999 );
- m_hRocketTrail->FollowEntity( this, "0" );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMissile::IgniteThink( void )
-{
- SetMoveType( MOVETYPE_FLY );
- SetModel("models/weapons/w_missile.mdl");
- UTIL_SetSize( this, vec3_origin, vec3_origin );
- RemoveSolidFlags( FSOLID_NOT_SOLID );
-
- //TODO: Play opening sound
-
- Vector vecForward;
-
- EmitSound( "Missile.Ignite" );
-
- AngleVectors( GetLocalAngles(), &vecForward );
- SetAbsVelocity( vecForward * RPG_SPEED );
-
- SetThink( &CMissile::SeekThink );
- SetNextThink( gpGlobals->curtime );
-
- if ( m_hOwner && m_hOwner->GetOwner() )
- {
- CBasePlayer *pPlayer = ToBasePlayer( m_hOwner->GetOwner() );
-
- color32 white = { 255,225,205,64 };
- UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN );
- }
-
- CreateSmokeTrail();
-}
-
-
-//-----------------------------------------------------------------------------
-// Gets the shooting position
-//-----------------------------------------------------------------------------
-void CMissile::GetShootPosition( CLaserDot *pLaserDot, Vector *pShootPosition )
-{
- if ( pLaserDot->GetOwnerEntity() != NULL )
- {
- //FIXME: Do we care this isn't exactly the muzzle position?
- *pShootPosition = pLaserDot->GetOwnerEntity()->WorldSpaceCenter();
- }
- else
- {
- *pShootPosition = pLaserDot->GetChasePosition();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-#define RPG_HOMING_SPEED 0.125f
-
-void CMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed )
-{
- *pHomingSpeed = RPG_HOMING_SPEED;
- if ( pLaserDot->GetTargetEntity() )
- {
- *pActualDotPosition = pLaserDot->GetChasePosition();
- return;
- }
-
- Vector vLaserStart;
- GetShootPosition( pLaserDot, &vLaserStart );
-
- //Get the laser's vector
- Vector vLaserDir;
- VectorSubtract( pLaserDot->GetChasePosition(), vLaserStart, vLaserDir );
-
- //Find the length of the current laser
- float flLaserLength = VectorNormalize( vLaserDir );
-
- //Find the length from the missile to the laser's owner
- float flMissileLength = GetAbsOrigin().DistTo( vLaserStart );
-
- //Find the length from the missile to the laser's position
- Vector vecTargetToMissile;
- VectorSubtract( GetAbsOrigin(), pLaserDot->GetChasePosition(), vecTargetToMissile );
- float flTargetLength = VectorNormalize( vecTargetToMissile );
-
- // See if we should chase the line segment nearest us
- if ( ( flMissileLength < flLaserLength ) || ( flTargetLength <= 512.0f ) )
- {
- *pActualDotPosition = UTIL_PointOnLineNearestPoint( vLaserStart, pLaserDot->GetChasePosition(), GetAbsOrigin() );
- *pActualDotPosition += ( vLaserDir * 256.0f );
- }
- else
- {
- // Otherwise chase the dot
- *pActualDotPosition = pLaserDot->GetChasePosition();
- }
-
-// NDebugOverlay::Line( pLaserDot->GetChasePosition(), vLaserStart, 0, 255, 0, true, 0.05f );
-// NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );
-// NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CMissile::SeekThink( void )
-{
- CBaseEntity *pBestDot = NULL;
- float flBestDist = MAX_TRACE_LENGTH;
- float dotDist;
-
- // If we have a grace period, go solid when it ends
- if ( m_flGracePeriodEndsAt )
- {
- if ( m_flGracePeriodEndsAt < gpGlobals->curtime )
- {
- RemoveSolidFlags( FSOLID_NOT_SOLID );
- m_flGracePeriodEndsAt = 0;
- }
- }
-
- //Search for all dots relevant to us
- for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext )
- {
- if ( !pEnt->IsOn() )
- continue;
-
- if ( pEnt->GetOwnerEntity() != GetOwnerEntity() )
- continue;
-
- dotDist = (GetAbsOrigin() - pEnt->GetAbsOrigin()).Length();
-
- //Find closest
- if ( dotDist < flBestDist )
- {
- pBestDot = pEnt;
- flBestDist = dotDist;
- }
- }
-
- //If we have a dot target
- if ( pBestDot == NULL )
- {
- //Think as soon as possible
- SetNextThink( gpGlobals->curtime );
- return;
- }
-
- CLaserDot *pLaserDot = (CLaserDot *)pBestDot;
- Vector targetPos;
-
- float flHomingSpeed;
- Vector vecLaserDotPosition;
- ComputeActualDotPosition( pLaserDot, &targetPos, &flHomingSpeed );
-
- if ( IsSimulatingOnAlternateTicks() )
- flHomingSpeed *= 2;
-
- Vector vTargetDir;
- VectorSubtract( targetPos, GetAbsOrigin(), vTargetDir );
- float flDist = VectorNormalize( vTargetDir );
-
- Vector vDir = GetAbsVelocity();
- float flSpeed = VectorNormalize( vDir );
- Vector vNewVelocity = vDir;
- if ( gpGlobals->frametime > 0.0f )
- {
- if ( flSpeed != 0 )
- {
- vNewVelocity = ( flHomingSpeed * vTargetDir ) + ( ( 1 - flHomingSpeed ) * vDir );
-
- // This computation may happen to cancel itself out exactly. If so, slam to targetdir.
- if ( VectorNormalize( vNewVelocity ) < 1e-3 )
- {
- vNewVelocity = (flDist != 0) ? vTargetDir : vDir;
- }
- }
- else
- {
- vNewVelocity = vTargetDir;
- }
- }
-
- QAngle finalAngles;
- VectorAngles( vNewVelocity, finalAngles );
- SetAbsAngles( finalAngles );
-
- vNewVelocity *= flSpeed;
- SetAbsVelocity( vNewVelocity );
-
- if( GetAbsVelocity() == vec3_origin )
- {
- // Strange circumstances have brought this missile to halt. Just blow it up.
- Explode();
- return;
- }
-
- // Think as soon as possible
- SetNextThink( gpGlobals->curtime );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-// Input : &vecOrigin -
-// &vecAngles -
-// NULL -
-//
-// Output : CMissile
-//-----------------------------------------------------------------------------
-CMissile *CMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner = NULL )
-{
- //CMissile *pMissile = (CMissile *)CreateEntityByName("rpg_missile" );
- CMissile *pMissile = (CMissile *) CBaseEntity::Create( "rpg_missile", vecOrigin, vecAngles, CBaseEntity::Instance( pentOwner ) );
- pMissile->SetOwnerEntity( Instance( pentOwner ) );
- pMissile->Spawn();
- pMissile->AddEffects( EF_NOSHADOW );
-
- Vector vecForward;
- AngleVectors( vecAngles, &vecForward );
-
- pMissile->SetAbsVelocity( vecForward * 300 + Vector( 0,0, 128 ) );
-
- return pMissile;
-}
-
-
-
-//-----------------------------------------------------------------------------
-// This entity is used to create little force boxes that the helicopter
-// should avoid.
-//-----------------------------------------------------------------------------
-class CInfoAPCMissileHint : public CBaseEntity
-{
- DECLARE_DATADESC();
-
-public:
- DECLARE_CLASS( CInfoAPCMissileHint, CBaseEntity );
-
- virtual void Spawn( );
- virtual void Activate();
- virtual void UpdateOnRemove();
-
- static CBaseEntity *FindAimTarget( CBaseEntity *pMissile, const char *pTargetName,
- const Vector &vecCurrentTargetPos, const Vector &vecCurrentTargetVel );
-
-private:
- EHANDLE m_hTarget;
-
- typedef CHandle<CInfoAPCMissileHint> APCMissileHintHandle_t;
- static CUtlVector< APCMissileHintHandle_t > s_APCMissileHints;
-};
-
-
-//-----------------------------------------------------------------------------
-//
-// This entity is used to create little force boxes that the helicopters should avoid.
-//
-//-----------------------------------------------------------------------------
-CUtlVector< CInfoAPCMissileHint::APCMissileHintHandle_t > CInfoAPCMissileHint::s_APCMissileHints;
-
-LINK_ENTITY_TO_CLASS( info_apc_missile_hint, CInfoAPCMissileHint );
-
-BEGIN_DATADESC( CInfoAPCMissileHint )
- DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Spawn, remove
-//-----------------------------------------------------------------------------
-void CInfoAPCMissileHint::Spawn( )
-{
- SetModel( STRING( GetModelName() ) );
- SetSolid( SOLID_BSP );
- AddSolidFlags( FSOLID_NOT_SOLID );
- AddEffects( EF_NODRAW );
-}
-
-void CInfoAPCMissileHint::Activate( )
-{
- BaseClass::Activate();
-
- m_hTarget = gEntList.FindEntityByName( NULL, m_target );
- if ( m_hTarget == NULL )
- {
- DevWarning( "%s: Could not find target '%s'!\n", GetClassname(), STRING( m_target ) );
- }
- else
- {
- s_APCMissileHints.AddToTail( this );
- }
-}
-
-void CInfoAPCMissileHint::UpdateOnRemove( )
-{
- s_APCMissileHints.FindAndRemove( this );
- BaseClass::UpdateOnRemove();
-}
-
-
-//-----------------------------------------------------------------------------
-// Where are how should we avoid?
-//-----------------------------------------------------------------------------
-#define HINT_PREDICTION_TIME 3.0f
-
-CBaseEntity *CInfoAPCMissileHint::FindAimTarget( CBaseEntity *pMissile, const char *pTargetName,
- const Vector &vecCurrentEnemyPos, const Vector &vecCurrentEnemyVel )
-{
- if ( !pTargetName )
- return NULL;
-
- float flOOSpeed = pMissile->GetAbsVelocity().Length();
- if ( flOOSpeed != 0.0f )
- {
- flOOSpeed = 1.0f / flOOSpeed;
- }
-
- for ( int i = s_APCMissileHints.Count(); --i >= 0; )
- {
- CInfoAPCMissileHint *pHint = s_APCMissileHints[i];
- if ( !pHint->NameMatches( pTargetName ) )
- continue;
-
- if ( !pHint->m_hTarget )
- continue;
-
- Vector vecMissileToHint, vecMissileToEnemy;
- VectorSubtract( pHint->m_hTarget->WorldSpaceCenter(), pMissile->GetAbsOrigin(), vecMissileToHint );
- VectorSubtract( vecCurrentEnemyPos, pMissile->GetAbsOrigin(), vecMissileToEnemy );
- float flDistMissileToHint = VectorNormalize( vecMissileToHint );
- VectorNormalize( vecMissileToEnemy );
- if ( DotProduct( vecMissileToHint, vecMissileToEnemy ) < 0.866f )
- continue;
-
- // Determine when the target will be inside the volume.
- // Project at most 3 seconds in advance
- Vector vecRayDelta;
- VectorMultiply( vecCurrentEnemyVel, HINT_PREDICTION_TIME, vecRayDelta );
-
- BoxTraceInfo_t trace;
- if ( !IntersectRayWithOBB( vecCurrentEnemyPos, vecRayDelta, pHint->CollisionProp()->CollisionToWorldTransform(),
- pHint->CollisionProp()->OBBMins(), pHint->CollisionProp()->OBBMaxs(), 0.0f, &trace ))
- {
- continue;
- }
-
- // Determine the amount of time it would take the missile to reach the target
- // If we can reach the target within the time it takes for the enemy to reach the
- float tSqr = flDistMissileToHint * flOOSpeed / HINT_PREDICTION_TIME;
- if ( (tSqr < (trace.t1 * trace.t1)) || (tSqr > (trace.t2 * trace.t2)) )
- continue;
-
- return pHint->m_hTarget;
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-// a list of missiles to search quickly
-//-----------------------------------------------------------------------------
-CEntityClassList<CAPCMissile> g_APCMissileList;
-template <> CAPCMissile *CEntityClassList<CAPCMissile>::m_pClassList = NULL;
-CAPCMissile *GetAPCMissileList()
-{
- return g_APCMissileList.m_pClassList;
-}
-
-//-----------------------------------------------------------------------------
-// Finds apc missiles in cone
-//-----------------------------------------------------------------------------
-CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDirection, float flAngle )
-{
- float flCosAngle = cos( DEG2RAD( flAngle ) );
- for( CAPCMissile *pEnt = GetAPCMissileList(); pEnt != NULL; pEnt = pEnt->m_pNext )
- {
- if ( !pEnt->IsSolid() )
- continue;
-
- Vector vecDelta;
- VectorSubtract( pEnt->GetAbsOrigin(), vecOrigin, vecDelta );
- VectorNormalize( vecDelta );
- float flDot = DotProduct( vecDelta, vecDirection );
- if ( flDot > flCosAngle )
- return pEnt;
- }
-
- return NULL;
-}
-
-
-//-----------------------------------------------------------------------------
-//
-// Specialized version of the missile
-//
-//-----------------------------------------------------------------------------
-#define MAX_HOMING_DISTANCE 2250.0f
-#define MIN_HOMING_DISTANCE 1250.0f
-#define MAX_NEAR_HOMING_DISTANCE 1750.0f
-#define MIN_NEAR_HOMING_DISTANCE 1000.0f
-#define DOWNWARD_BLEND_TIME_START 0.2f
-#define MIN_HEIGHT_DIFFERENCE 250.0f
-#define MAX_HEIGHT_DIFFERENCE 550.0f
-#define CORRECTION_TIME 0.2f
-#define APC_LAUNCH_HOMING_SPEED 0.1f
-#define APC_HOMING_SPEED 0.025f
-#define HOMING_SPEED_ACCEL 0.01f
-
-BEGIN_DATADESC( CAPCMissile )
-
- DEFINE_FIELD( m_flReachedTargetTime, FIELD_TIME ),
- DEFINE_FIELD( m_flIgnitionTime, FIELD_TIME ),
- DEFINE_FIELD( m_bGuidingDisabled, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_hSpecificTarget, FIELD_EHANDLE ),
- DEFINE_FIELD( m_strHint, FIELD_STRING ),
- DEFINE_FIELD( m_flLastHomingSpeed, FIELD_FLOAT ),
-// DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ),
-
- DEFINE_THINKFUNC( BeginSeekThink ),
- DEFINE_THINKFUNC( AugerStartThink ),
- DEFINE_THINKFUNC( ExplodeThink ),
-
- DEFINE_FUNCTION( APCMissileTouch ),
-
-END_DATADESC()
-
-LINK_ENTITY_TO_CLASS( apc_missile, CAPCMissile );
-
-CAPCMissile *CAPCMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, CBaseEntity *pOwner )
-{
- CAPCMissile *pMissile = (CAPCMissile *)CBaseEntity::Create( "apc_missile", vecOrigin, vecAngles, pOwner );
- pMissile->SetOwnerEntity( pOwner );
- pMissile->Spawn();
- pMissile->SetAbsVelocity( vecVelocity );
- pMissile->AddFlag( FL_NOTARGET );
- pMissile->AddEffects( EF_NOSHADOW );
- return pMissile;
-}
-
-
-//-----------------------------------------------------------------------------
-// Constructor, destructor
-//-----------------------------------------------------------------------------
-CAPCMissile::CAPCMissile()
-{
- g_APCMissileList.Insert( this );
-}
-
-CAPCMissile::~CAPCMissile()
-{
- g_APCMissileList.Remove( this );
-}
-
-
-//-----------------------------------------------------------------------------
-// Shared initialization code
-//-----------------------------------------------------------------------------
-void CAPCMissile::Init()
-{
- SetMoveType( MOVETYPE_FLY );
- SetModel("models/weapons/w_missile.mdl");
- UTIL_SetSize( this, vec3_origin, vec3_origin );
- CreateSmokeTrail();
- SetTouch( &CAPCMissile::APCMissileTouch );
- m_flLastHomingSpeed = APC_HOMING_SPEED;
-}
-
-
-//-----------------------------------------------------------------------------
-// For hitting a specific target
-//-----------------------------------------------------------------------------
-void CAPCMissile::AimAtSpecificTarget( CBaseEntity *pTarget )
-{
- m_hSpecificTarget = pTarget;
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pOther -
-//-----------------------------------------------------------------------------
-void CAPCMissile::APCMissileTouch( CBaseEntity *pOther )
-{
- Assert( pOther );
- if ( !pOther->IsSolid() && !pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
- return;
-
- Explode();
-}
-
-
-//-----------------------------------------------------------------------------
-// Specialized version of the missile
-//-----------------------------------------------------------------------------
-void CAPCMissile::IgniteDelay( void )
-{
- m_flIgnitionTime = gpGlobals->curtime + 0.3f;
-
- SetThink( &CAPCMissile::BeginSeekThink );
- SetNextThink( m_flIgnitionTime );
- Init();
- AddSolidFlags( FSOLID_NOT_SOLID );
-}
-
-void CAPCMissile::AugerDelay( float flDelay )
-{
- m_flIgnitionTime = gpGlobals->curtime;
- SetThink( &CAPCMissile::AugerStartThink );
- SetNextThink( gpGlobals->curtime + flDelay );
- Init();
- DisableGuiding();
-}
-
-void CAPCMissile::AugerStartThink()
-{
- if ( m_hRocketTrail != NULL )
- {
- m_hRocketTrail->m_bDamaged = true;
- }
- m_flAugerTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );
- SetThink( &CAPCMissile::AugerThink );
- SetNextThink( gpGlobals->curtime );
-}
-
-void CAPCMissile::ExplodeDelay( float flDelay )
-{
- m_flIgnitionTime = gpGlobals->curtime;
- SetThink( &CAPCMissile::ExplodeThink );
- SetNextThink( gpGlobals->curtime + flDelay );
- Init();
- DisableGuiding();
-}
-
-
-void CAPCMissile::BeginSeekThink( void )
-{
- RemoveSolidFlags( FSOLID_NOT_SOLID );
- SetThink( &CAPCMissile::SeekThink );
- SetNextThink( gpGlobals->curtime );
-}
-
-void CAPCMissile::ExplodeThink()
-{
- DoExplosion();
-}
-
-//-----------------------------------------------------------------------------
-// Health lost at which augering starts
-//-----------------------------------------------------------------------------
-int CAPCMissile::AugerHealth()
-{
- return m_iMaxHealth - 25;
-}
-
-
-//-----------------------------------------------------------------------------
-// Health lost at which augering starts
-//-----------------------------------------------------------------------------
-void CAPCMissile::DisableGuiding()
-{
- m_bGuidingDisabled = true;
-}
-
-
-//-----------------------------------------------------------------------------
-// Guidance hints
-//-----------------------------------------------------------------------------
-void CAPCMissile::SetGuidanceHint( const char *pHintName )
-{
- m_strHint = MAKE_STRING( pHintName );
-}
-
-
-//-----------------------------------------------------------------------------
-// The actual explosion
-//-----------------------------------------------------------------------------
-void CAPCMissile::DoExplosion( void )
-{
- if ( GetWaterLevel() != 0 )
- {
- CEffectData data;
- data.m_vOrigin = WorldSpaceCenter();
- data.m_flMagnitude = 128;
- data.m_flScale = 128;
- data.m_fFlags = 0;
- DispatchEffect( "WaterSurfaceExplosion", data );
- }
- else
- {
- ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(),
- APC_MISSILE_DAMAGE, 100, true, 20000 );
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAPCMissile::ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition )
-{
- Vector vecTarget = pTarget->BodyTarget( vecShootPosition, false );
- float flShotSpeed = GetAbsVelocity().Length();
- if ( flShotSpeed == 0 )
- {
- *pLeadPosition = vecTarget;
- return;
- }
-
- Vector vecVelocity = pTarget->GetSmoothedVelocity();
- vecVelocity.z = 0.0f;
- float flTargetSpeed = VectorNormalize( vecVelocity );
- Vector vecDelta;
- VectorSubtract( vecShootPosition, vecTarget, vecDelta );
- float flTargetToShooter = VectorNormalize( vecDelta );
- float flCosTheta = DotProduct( vecDelta, vecVelocity );
-
- // Law of cosines... z^2 = x^2 + y^2 - 2xy cos Theta
- // where z = flShooterToPredictedTargetPosition = flShotSpeed * predicted time
- // x = flTargetSpeed * predicted time
- // y = flTargetToShooter
- // solve for predicted time using at^2 + bt + c = 0, t = (-b +/- sqrt( b^2 - 4ac )) / 2a
- float a = flTargetSpeed * flTargetSpeed - flShotSpeed * flShotSpeed;
- float b = -2.0f * flTargetToShooter * flCosTheta * flTargetSpeed;
- float c = flTargetToShooter * flTargetToShooter;
-
- float flDiscrim = b*b - 4*a*c;
- if (flDiscrim < 0)
- {
- *pLeadPosition = vecTarget;
- return;
- }
-
- flDiscrim = sqrt(flDiscrim);
- float t = (-b + flDiscrim) / (2.0f * a);
- float t2 = (-b - flDiscrim) / (2.0f * a);
- if ( t < t2 )
- {
- t = t2;
- }
-
- if ( t <= 0.0f )
- {
- *pLeadPosition = vecTarget;
- return;
- }
-
- VectorMA( vecTarget, flTargetSpeed * t, vecVelocity, *pLeadPosition );
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CAPCMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed )
-{
- if ( m_bGuidingDisabled )
- {
- *pActualDotPosition = GetAbsOrigin();
- *pHomingSpeed = 0.0f;
- m_flLastHomingSpeed = *pHomingSpeed;
- return;
- }
-
- if ( ( m_strHint != NULL_STRING ) && (!m_hSpecificTarget) )
- {
- Vector vecOrigin, vecVelocity;
- CBaseEntity *pTarget = pLaserDot->GetTargetEntity();
- if ( pTarget )
- {
- vecOrigin = pTarget->BodyTarget( GetAbsOrigin(), false );
- vecVelocity = pTarget->GetSmoothedVelocity();
- }
- else
- {
- vecOrigin = pLaserDot->GetChasePosition();
- vecVelocity = vec3_origin;
- }
-
- m_hSpecificTarget = CInfoAPCMissileHint::FindAimTarget( this, STRING( m_strHint ), vecOrigin, vecVelocity );
- }
-
- CBaseEntity *pLaserTarget = m_hSpecificTarget ? m_hSpecificTarget.Get() : pLaserDot->GetTargetEntity();
- if ( !pLaserTarget )
- {
- BaseClass::ComputeActualDotPosition( pLaserDot, pActualDotPosition, pHomingSpeed );
- m_flLastHomingSpeed = *pHomingSpeed;
- return;
- }
-
- if ( pLaserTarget->ClassMatches( "npc_bullseye" ) )
- {
- if ( m_flLastHomingSpeed != RPG_HOMING_SPEED )
- {
- if (m_flLastHomingSpeed > RPG_HOMING_SPEED)
- {
- m_flLastHomingSpeed -= HOMING_SPEED_ACCEL * UTIL_GetSimulationInterval();
- if ( m_flLastHomingSpeed < RPG_HOMING_SPEED )
- {
- m_flLastHomingSpeed = RPG_HOMING_SPEED;
- }
- }
- else
- {
- m_flLastHomingSpeed += HOMING_SPEED_ACCEL * UTIL_GetSimulationInterval();
- if ( m_flLastHomingSpeed > RPG_HOMING_SPEED )
- {
- m_flLastHomingSpeed = RPG_HOMING_SPEED;
- }
- }
- }
- *pHomingSpeed = m_flLastHomingSpeed;
- *pActualDotPosition = pLaserTarget->WorldSpaceCenter();
- return;
- }
-
- Vector vLaserStart;
- GetShootPosition( pLaserDot, &vLaserStart );
- *pHomingSpeed = APC_LAUNCH_HOMING_SPEED;
-
- //Get the laser's vector
- Vector vecTargetPosition = pLaserTarget->BodyTarget( GetAbsOrigin(), false );
-
- // Compute leading position
- Vector vecLeadPosition;
- ComputeLeadingPosition( GetAbsOrigin(), pLaserTarget, &vecLeadPosition );
-
- Vector vecTargetToMissile, vecTargetToShooter;
- VectorSubtract( GetAbsOrigin(), vecTargetPosition, vecTargetToMissile );
- VectorSubtract( vLaserStart, vecTargetPosition, vecTargetToShooter );
-
- *pActualDotPosition = vecLeadPosition;
-
- float flMinHomingDistance = MIN_HOMING_DISTANCE;
- float flMaxHomingDistance = MAX_HOMING_DISTANCE;
- float flBlendTime = gpGlobals->curtime - m_flIgnitionTime;
- if ( flBlendTime > DOWNWARD_BLEND_TIME_START )
- {
- if ( m_flReachedTargetTime != 0.0f )
- {
- *pHomingSpeed = APC_HOMING_SPEED;
- float flDeltaTime = clamp( gpGlobals->curtime - m_flReachedTargetTime, 0.0f, CORRECTION_TIME );
- *pHomingSpeed = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, 0.2f, *pHomingSpeed );
- flMinHomingDistance = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, MIN_NEAR_HOMING_DISTANCE, flMinHomingDistance );
- flMaxHomingDistance = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, MAX_NEAR_HOMING_DISTANCE, flMaxHomingDistance );
- }
- else
- {
- flMinHomingDistance = MIN_NEAR_HOMING_DISTANCE;
- flMaxHomingDistance = MAX_NEAR_HOMING_DISTANCE;
- Vector vecDelta;
- VectorSubtract( GetAbsOrigin(), *pActualDotPosition, vecDelta );
- if ( vecDelta.z > MIN_HEIGHT_DIFFERENCE )
- {
- float flClampedHeight = clamp( vecDelta.z, MIN_HEIGHT_DIFFERENCE, MAX_HEIGHT_DIFFERENCE );
- float flHeightAdjustFactor = SimpleSplineRemapVal( flClampedHeight, MIN_HEIGHT_DIFFERENCE, MAX_HEIGHT_DIFFERENCE, 0.0f, 1.0f );
-
- vecDelta.z = 0.0f;
- float flDist = VectorNormalize( vecDelta );
-
- float flForwardOffset = 2000.0f;
- if ( flDist > flForwardOffset )
- {
- Vector vecNewPosition;
- VectorMA( GetAbsOrigin(), -flForwardOffset, vecDelta, vecNewPosition );
- vecNewPosition.z = pActualDotPosition->z;
-
- VectorLerp( *pActualDotPosition, vecNewPosition, flHeightAdjustFactor, *pActualDotPosition );
- }
- }
- else
- {
- m_flReachedTargetTime = gpGlobals->curtime;
- }
- }
-
- // Allows for players right at the edge of rocket range to be threatened
- if ( flBlendTime > 0.6f )
- {
- float flTargetLength = GetAbsOrigin().DistTo( pLaserTarget->WorldSpaceCenter() );
- flTargetLength = clamp( flTargetLength, flMinHomingDistance, flMaxHomingDistance );
- *pHomingSpeed = SimpleSplineRemapVal( flTargetLength, flMaxHomingDistance, flMinHomingDistance, *pHomingSpeed, 0.01f );
- }
- }
-
- float flDot = DotProduct2D( vecTargetToShooter.AsVector2D(), vecTargetToMissile.AsVector2D() );
- if ( ( flDot < 0 ) || m_bGuidingDisabled )
- {
- *pHomingSpeed = 0.0f;
- }
-
- m_flLastHomingSpeed = *pHomingSpeed;
-
-// NDebugOverlay::Line( vecLeadPosition, GetAbsOrigin(), 0, 255, 0, true, 0.05f );
-// NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );
-// NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );
-}
-
-#endif
-
-#define RPG_BEAM_SPRITE "effects/laser1.vmt"
-#define RPG_BEAM_SPRITE_NOZ "effects/laser1_noz.vmt"
-#define RPG_LASER_SPRITE "sprites/redglow1"
-
-//=============================================================================
-// RPG
-//=============================================================================
-
-LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG );
-PRECACHE_WEAPON_REGISTER(weapon_rpg);
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRPG, DT_WeaponRPG )
-
-#ifdef CLIENT_DLL
-void RecvProxy_MissileDied( const CRecvProxyData *pData, void *pStruct, void *pOut )
-{
- CWeaponRPG *pRPG = ((CWeaponRPG*)pStruct);
-
- RecvProxy_IntToEHandle( pData, pStruct, pOut );
-
- CBaseEntity *pNewMissile = pRPG->GetMissile();
-
- if ( pNewMissile == NULL )
- {
- if ( pRPG->GetOwner() && pRPG->GetOwner()->GetActiveWeapon() == pRPG )
- {
- pRPG->NotifyRocketDied();
- }
- }
-}
-
-#endif
-
-BEGIN_NETWORK_TABLE( CWeaponRPG, DT_WeaponRPG )
-#ifdef CLIENT_DLL
- RecvPropBool( RECVINFO( m_bInitialStateUpdate ) ),
- RecvPropBool( RECVINFO( m_bGuiding ) ),
- RecvPropBool( RECVINFO( m_bHideGuiding ) ),
- RecvPropEHandle( RECVINFO( m_hMissile ), RecvProxy_MissileDied ),
- RecvPropVector( RECVINFO( m_vecLaserDot ) ),
-#else
- SendPropBool( SENDINFO( m_bInitialStateUpdate ) ),
- SendPropBool( SENDINFO( m_bGuiding ) ),
- SendPropBool( SENDINFO( m_bHideGuiding ) ),
- SendPropEHandle( SENDINFO( m_hMissile ) ),
- SendPropVector( SENDINFO( m_vecLaserDot ) ),
-#endif
-END_NETWORK_TABLE()
-
-#ifdef CLIENT_DLL
-
-BEGIN_PREDICTION_DATA( CWeaponRPG )
- DEFINE_PRED_FIELD( m_bInitialStateUpdate, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bGuiding, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bHideGuiding, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
-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 },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponRPG);
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CWeaponRPG::CWeaponRPG()
-{
- m_bReloadsSingly = true;
- m_bInitialStateUpdate= false;
- m_bHideGuiding = false;
- m_bGuiding = false;
-
- m_fMinRange1 = m_fMinRange2 = 40*12;
- m_fMaxRange1 = m_fMaxRange2 = 500*12;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-CWeaponRPG::~CWeaponRPG()
-{
-#ifndef CLIENT_DLL
- if ( m_hLaserDot != NULL )
- {
- UTIL_Remove( m_hLaserDot );
- m_hLaserDot = NULL;
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::Precache( void )
-{
- BaseClass::Precache();
-
- PrecacheScriptSound( "Missile.Ignite" );
- PrecacheScriptSound( "Missile.Accelerate" );
-
- // Laser dot...
- PrecacheModel( "sprites/redglow1.vmt" );
- PrecacheModel( RPG_LASER_SPRITE );
- PrecacheModel( RPG_BEAM_SPRITE );
- PrecacheModel( RPG_BEAM_SPRITE_NOZ );
-
-#ifndef CLIENT_DLL
- UTIL_PrecacheOther( "rpg_missile" );
-#endif
-
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::Activate( void )
-{
- BaseClass::Activate();
-
- // Restore the laser pointer after transition
- if ( m_bGuiding )
- {
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- if ( pOwner->GetActiveWeapon() == this )
- {
- StartGuiding();
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::HasAnyAmmo( void )
-{
- if ( m_hMissile != NULL )
- return true;
-
- return BaseClass::HasAnyAmmo();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::WeaponShouldBeLowered( void )
-{
- // Lower us if we're out of ammo
- if ( !HasAnyAmmo() )
- return true;
-
- return BaseClass::WeaponShouldBeLowered();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-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;
-
- // Can't be reloading
- if ( GetActivity() == ACT_VM_RELOAD )
- return;
-
- Vector vecOrigin;
- Vector vecForward;
-
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner == NULL )
- return;
-
- Vector vForward, vRight, vUp;
-
- pOwner->EyeVectors( &vForward, &vRight, &vUp );
-
- Vector muzzlePoint = pOwner->Weapon_ShootPosition() + vForward * 12.0f + vRight * 6.0f + vUp * -3.0f;
-
-#ifndef CLIENT_DLL
- QAngle vecAngles;
- VectorAngles( vForward, vecAngles );
-
- CMissile *pMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
- pMissile->m_hOwner = this;
-
- // If the shot is clear to the player, give the missile a grace period
- trace_t tr;
- Vector vecEye = pOwner->EyePosition();
- UTIL_TraceLine( vecEye, vecEye + vForward * 128, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
- if ( tr.fraction == 1.0 )
- {
- pMissile->SetGracePeriod( 0.3 );
- }
-
- pMissile->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
-
- m_hMissile = pMissile;
-#endif
-
- DecrementAmmo( GetOwner() );
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
- WeaponSound( SINGLE );
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : *pOwner -
-//-----------------------------------------------------------------------------
-void CWeaponRPG::DecrementAmmo( CBaseCombatCharacter *pOwner )
-{
- // Take away our primary ammo type
- pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : state -
-//-----------------------------------------------------------------------------
-void CWeaponRPG::SuppressGuiding( bool state )
-{
- m_bHideGuiding = state;
-
-#ifndef CLIENT_DLL
-
- if ( m_hLaserDot == NULL )
- {
- StartGuiding();
-
- //STILL!?
- if ( m_hLaserDot == NULL )
- return;
- }
-
- if ( state )
- {
- m_hLaserDot->TurnOff();
- }
- else
- {
- m_hLaserDot->TurnOn();
- }
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override this if we're guiding a missile currently
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::Lower( void )
-{
- if ( m_hMissile != NULL )
- return false;
-
- return BaseClass::Lower();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::ItemPostFrame( void )
-{
- BaseClass::ItemPostFrame();
-
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if ( pPlayer == NULL )
- return;
-
- //If we're pulling the weapon out for the first time, wait to draw the laser
- if ( ( m_bInitialStateUpdate ) && ( GetActivity() != ACT_VM_DRAW ) )
- {
- StartGuiding();
- m_bInitialStateUpdate = false;
- }
-
- // Supress our guiding effects if we're lowered
- if ( GetIdealActivity() == ACT_VM_IDLE_LOWERED )
- {
- SuppressGuiding();
- }
- else
- {
- SuppressGuiding( false );
- }
-
- //Move the laser
- UpdateLaserPosition();
-
- if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 && m_hMissile == NULL )
- {
- StopGuiding();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Vector
-//-----------------------------------------------------------------------------
-Vector CWeaponRPG::GetLaserPosition( void )
-{
-#ifndef CLIENT_DLL
- CreateLaserPointer();
-
- if ( m_hLaserDot != NULL )
- return m_hLaserDot->GetAbsOrigin();
-
- //FIXME: The laser dot sprite is not active, this code should not be allowed!
- assert(0);
-#endif
- return vec3_origin;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: NPC RPG users cheat and directly set the laser pointer's origin
-// Input : &vecTarget -
-//-----------------------------------------------------------------------------
-void CWeaponRPG::UpdateNPCLaserPosition( const Vector &vecTarget )
-{
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::SetNPCLaserPosition( const Vector &vecTarget )
-{
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-const Vector &CWeaponRPG::GetNPCLaserPosition( void )
-{
- return vec3_origin;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true if the rocket is being guided, false if it's dumb
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::IsGuiding( void )
-{
- return m_bGuiding;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::Deploy( void )
-{
- m_bInitialStateUpdate = true;
-
- return BaseClass::Deploy();
-}
-
-bool CWeaponRPG::CanHolster( void )
-{
- //Can't have an active missile out
- if ( m_hMissile != NULL )
- return false;
-
- return BaseClass::CanHolster();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::Holster( CBaseCombatWeapon *pSwitchingTo )
-{
- StopGuiding();
-
- return BaseClass::Holster( pSwitchingTo );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Turn on the guiding laser
-//-----------------------------------------------------------------------------
-void CWeaponRPG::StartGuiding( void )
-{
- // Don't start back up if we're overriding this
- if ( m_bHideGuiding )
- return;
-
- m_bGuiding = true;
-
-#ifndef CLIENT_DLL
- WeaponSound(SPECIAL1);
-
- CreateLaserPointer();
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Turn off the guiding laser
-//-----------------------------------------------------------------------------
-void CWeaponRPG::StopGuiding( void )
-{
- m_bGuiding = false;
-
-#ifndef CLIENT_DLL
-
- WeaponSound( SPECIAL2 );
-
- // Kill the dot completely
- if ( m_hLaserDot != NULL )
- {
- m_hLaserDot->TurnOff();
- UTIL_Remove( m_hLaserDot );
- m_hLaserDot = NULL;
- }
-#else
- if ( m_pBeam )
- {
- //Tell it to die right away and let the beam code free it.
- m_pBeam->brightness = 0.0f;
- m_pBeam->flags &= ~FBEAM_FOREVER;
- m_pBeam->die = gpGlobals->curtime - 0.1;
- m_pBeam = NULL;
- }
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Toggle the guiding laser
-//-----------------------------------------------------------------------------
-void CWeaponRPG::ToggleGuiding( void )
-{
- if ( IsGuiding() )
- {
- StopGuiding();
- }
- else
- {
- StartGuiding();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::Drop( const Vector &vecVelocity )
-{
- StopGuiding();
-
- BaseClass::Drop( vecVelocity );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::UpdateLaserPosition( Vector vecMuzzlePos, Vector vecEndPos )
-{
-
-#ifndef CLIENT_DLL
- if ( vecMuzzlePos == vec3_origin || vecEndPos == vec3_origin )
- {
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
- if ( !pPlayer )
- return;
-
- vecMuzzlePos = pPlayer->Weapon_ShootPosition();
- Vector forward;
- pPlayer->EyeVectors( &forward );
- vecEndPos = vecMuzzlePos + ( forward * MAX_TRACE_LENGTH );
- }
-
- //Move the laser dot, if active
- trace_t tr;
-
- // Trace out for the endpoint
- UTIL_TraceLine( vecMuzzlePos, vecEndPos, (MASK_SHOT & ~CONTENTS_WINDOW), GetOwner(), 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;
-
- if ( ( pHit != NULL ) && ( pHit->m_takedamage ) )
- {
- m_hLaserDot->SetTargetEntity( pHit );
- }
- else
- {
- m_hLaserDot->SetTargetEntity( NULL );
- }
- }
- else
- {
- m_hLaserDot->SetTargetEntity( NULL );
- }
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::CreateLaserPointer( void )
-{
-#ifndef CLIENT_DLL
- if ( m_hLaserDot != NULL )
- return;
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
- return;
-
- m_hLaserDot = CLaserDot::Create( GetAbsOrigin(), GetOwner() );
- m_hLaserDot->TurnOff();
-
- UpdateLaserPosition();
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponRPG::NotifyRocketDied( void )
-{
- m_hMissile = NULL;
-
- if ( GetActivity() == ACT_VM_RELOAD )
- return;
-
- Reload();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::Reload( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return false;
-
- if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
- return false;
-
- WeaponSound( RELOAD );
-
- SendWeaponAnim( ACT_VM_RELOAD );
-
- return true;
-}
-
-#ifdef CLIENT_DLL
-
-#define RPG_MUZZLE_ATTACHMENT 1
-#define RPG_GUIDE_ATTACHMENT 2
-#define RPG_GUIDE_TARGET_ATTACHMENT 3
-
-#define RPG_GUIDE_ATTACHMENT_3RD 4
-#define RPG_GUIDE_TARGET_ATTACHMENT_3RD 5
-
-#define RPG_LASER_BEAM_LENGTH 128
-
-extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns the attachment point on either the world or viewmodel
-// This should really be worked into the CBaseCombatWeapon class!
-//-----------------------------------------------------------------------------
-void CWeaponRPG::GetWeaponAttachment( int attachmentId, Vector &outVector, Vector *dir /*= NULL*/ )
-{
- QAngle angles;
-
- if ( ShouldDrawUsingViewModel() )
- {
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner != NULL )
- {
- pOwner->GetViewModel()->GetAttachment( attachmentId, outVector, angles );
- ::FormatViewModelAttachment( outVector, true );
- }
- }
- else
- {
- // We offset the IDs to make them correct for our world model
- BaseClass::GetAttachment( attachmentId, outVector, angles );
- }
-
- // Supply the direction, if requested
- if ( dir != NULL )
- {
- AngleVectors( angles, dir, NULL, NULL );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Setup our laser beam
-//-----------------------------------------------------------------------------
-void CWeaponRPG::InitBeam( void )
-{
- if ( m_pBeam != NULL )
- return;
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
- return;
-
-
- BeamInfo_t beamInfo;
-
- CBaseEntity *pEntity = NULL;
-
- if ( ShouldDrawUsingViewModel() )
- {
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
-
- if ( pOwner != NULL )
- {
- pEntity = pOwner->GetViewModel();
- }
- }
- else
- {
- pEntity = this;
- }
-
- beamInfo.m_pStartEnt = pEntity;
- beamInfo.m_pEndEnt = pEntity;
- beamInfo.m_nType = TE_BEAMPOINTS;
- beamInfo.m_vecStart = vec3_origin;
- beamInfo.m_vecEnd = vec3_origin;
-
- beamInfo.m_pszModelName = ( ShouldDrawUsingViewModel() ) ? RPG_BEAM_SPRITE_NOZ : RPG_BEAM_SPRITE;
-
- beamInfo.m_flHaloScale = 0.0f;
- beamInfo.m_flLife = 0.0f;
-
- if ( ShouldDrawUsingViewModel() )
- {
- beamInfo.m_flWidth = 2.0f;
- beamInfo.m_flEndWidth = 2.0f;
- beamInfo.m_nStartAttachment = RPG_GUIDE_ATTACHMENT;
- beamInfo.m_nEndAttachment = RPG_GUIDE_TARGET_ATTACHMENT;
- }
- else
- {
- beamInfo.m_flWidth = 1.0f;
- beamInfo.m_flEndWidth = 1.0f;
- beamInfo.m_nStartAttachment = RPG_GUIDE_ATTACHMENT_3RD;
- beamInfo.m_nEndAttachment = RPG_GUIDE_TARGET_ATTACHMENT_3RD;
- }
-
- beamInfo.m_flFadeLength = 0.0f;
- beamInfo.m_flAmplitude = 0;
- beamInfo.m_flBrightness = 255.0;
- beamInfo.m_flSpeed = 1.0f;
- beamInfo.m_nStartFrame = 0.0;
- beamInfo.m_flFrameRate = 30.0;
- beamInfo.m_flRed = 255.0;
- beamInfo.m_flGreen = 0.0;
- beamInfo.m_flBlue = 0.0;
- beamInfo.m_nSegments = 4;
- beamInfo.m_bRenderable = true;
- beamInfo.m_nFlags = (FBEAM_FOREVER|FBEAM_SHADEOUT);
-
- m_pBeam = beams->CreateBeamEntPoint( beamInfo );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw effects for our weapon
-//-----------------------------------------------------------------------------
-void CWeaponRPG::DrawEffects( void )
-{
- // Must be guiding and not hidden
- if ( !m_bGuiding || m_bHideGuiding )
- {
- if ( m_pBeam != NULL )
- {
- m_pBeam->brightness = 0;
- }
-
- return;
- }
-
- // Setup our sprite
- if ( m_hSpriteMaterial == NULL )
- {
- m_hSpriteMaterial.Init( RPG_LASER_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
- }
-
- // Setup our beam
- if ( m_hBeamMaterial == NULL )
- {
- m_hBeamMaterial.Init( RPG_BEAM_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
- }
-
- color32 color={255,255,255,255};
- Vector vecAttachment, vecDir;
- QAngle angles;
-
- float scale = 8.0f + random->RandomFloat( -2.0f, 2.0f );
-
- int attachmentID = ( ShouldDrawUsingViewModel() ) ? RPG_GUIDE_ATTACHMENT : RPG_GUIDE_ATTACHMENT_3RD;
-
- GetWeaponAttachment( attachmentID, vecAttachment, &vecDir );
-
- // Draw the sprite
- CMatRenderContextPtr pRenderContext( materials );
- pRenderContext->Bind( m_hSpriteMaterial, this );
- DrawSprite( vecAttachment, scale, scale, color );
-
- // Get the beam's run
- trace_t tr;
- UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * RPG_LASER_BEAM_LENGTH ), MASK_SHOT, GetOwner(), COLLISION_GROUP_NONE, &tr );
-
- InitBeam();
-
- if ( m_pBeam != NULL )
- {
- m_pBeam->fadeLength = RPG_LASER_BEAM_LENGTH * tr.fraction;
- m_pBeam->brightness = random->RandomInt( 128, 200 );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called on third-person weapon drawing
-//-----------------------------------------------------------------------------
-int CWeaponRPG::DrawModel( int flags )
-{
- // Only render these on the transparent pass
- if ( flags & STUDIO_TRANSPARENCY )
- {
- DrawEffects();
- return 1;
- }
-
- // Draw the model as normal
- return BaseClass::DrawModel( flags );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Called after first-person viewmodel is drawn
-//-----------------------------------------------------------------------------
-void CWeaponRPG::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
-{
- // Draw our laser effects
- DrawEffects();
-
- BaseClass::ViewModelDrawn( pBaseViewModel );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Used to determine sorting of model when drawn
-//-----------------------------------------------------------------------------
-bool CWeaponRPG::IsTranslucent( void )
-{
- // Must be guiding and not hidden
- if ( m_bGuiding && !m_bHideGuiding )
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Turns off effects when leaving the PVS
-//-----------------------------------------------------------------------------
-void CWeaponRPG::NotifyShouldTransmit( ShouldTransmitState_t state )
-{
- BaseClass::NotifyShouldTransmit(state);
-
- if ( state == SHOULDTRANSMIT_END )
- {
- if ( m_pBeam != NULL )
- {
- m_pBeam->brightness = 0.0f;
- }
- }
-}
-
-#endif //CLIENT_DLL
-
-
-//=============================================================================
-// Laser Dot
-//=============================================================================
-
-LINK_ENTITY_TO_CLASS( env_laserdot, CLaserDot );
-
-BEGIN_DATADESC( CLaserDot )
- DEFINE_FIELD( m_vecSurfaceNormal, FIELD_VECTOR ),
- DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
- DEFINE_FIELD( m_bVisibleLaserDot, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ),
-
- //DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ), // don't save - regenerated by constructor
-END_DATADESC()
-
-
-//-----------------------------------------------------------------------------
-// Finds missiles in cone
-//-----------------------------------------------------------------------------
-CBaseEntity *CreateLaserDot( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
-{
- return CLaserDot::Create( origin, pOwner, bVisibleDot );
-}
-
-void SetLaserDotTarget( CBaseEntity *pLaserDot, CBaseEntity *pTarget )
-{
- CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot );
- pDot->SetTargetEntity( pTarget );
-}
-
-void EnableLaserDot( CBaseEntity *pLaserDot, bool bEnable )
-{
- CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot );
- if ( bEnable )
- {
- pDot->TurnOn();
- }
- else
- {
- pDot->TurnOff();
- }
-}
-
-CLaserDot::CLaserDot( void )
-{
- m_hTargetEnt = NULL;
- m_bIsOn = true;
-#ifndef CLIENT_DLL
- g_LaserDotList.Insert( this );
-#endif
-}
-
-CLaserDot::~CLaserDot( void )
-{
-#ifndef CLIENT_DLL
- g_LaserDotList.Remove( this );
-#endif
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &origin -
-// Output : CLaserDot
-//-----------------------------------------------------------------------------
-CLaserDot *CLaserDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
-{
-#ifndef CLIENT_DLL
- CLaserDot *pLaserDot = (CLaserDot *) CBaseEntity::Create( "env_laserdot", origin, QAngle(0,0,0) );
-
- if ( pLaserDot == NULL )
- return NULL;
-
- pLaserDot->m_bVisibleLaserDot = bVisibleDot;
- pLaserDot->SetMoveType( MOVETYPE_NONE );
- pLaserDot->AddSolidFlags( FSOLID_NOT_SOLID );
- pLaserDot->AddEffects( EF_NOSHADOW );
- UTIL_SetSize( pLaserDot, -Vector(4,4,4), Vector(4,4,4) );
-
- pLaserDot->SetOwnerEntity( pOwner );
-
- pLaserDot->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
-
- if ( !bVisibleDot )
- {
- pLaserDot->MakeInvisible();
- }
-
- return pLaserDot;
-#else
- return NULL;
-#endif
-}
-
-void CLaserDot::SetLaserPosition( const Vector &origin, const Vector &normal )
-{
- SetAbsOrigin( origin );
- m_vecSurfaceNormal = normal;
-}
-
-Vector CLaserDot::GetChasePosition()
-{
- return GetAbsOrigin() - m_vecSurfaceNormal * 10;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CLaserDot::TurnOn( void )
-{
- m_bIsOn = true;
- if ( m_bVisibleLaserDot )
- {
- //BaseClass::TurnOn();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CLaserDot::TurnOff( void )
-{
- m_bIsOn = false;
- if ( m_bVisibleLaserDot )
- {
- //BaseClass::TurnOff();
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CLaserDot::MakeInvisible( void )
-{
-}
-
-#ifdef CLIENT_DLL
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw our sprite
-//-----------------------------------------------------------------------------
-int CLaserDot::DrawModel( int flags )
-{
- color32 color={255,255,255,255};
- Vector vecAttachment, vecDir;
- QAngle angles;
-
- float scale;
- Vector endPos;
-
- C_HL2MP_Player *pOwner = ToHL2MPPlayer( GetOwnerEntity() );
-
- if ( pOwner != NULL && pOwner->IsDormant() == false )
- {
- // Always draw the dot in front of our faces when in first-person
- if ( pOwner->IsLocalPlayer() )
- {
- // Take our view position and orientation
- vecAttachment = CurrentViewOrigin();
- vecDir = CurrentViewForward();
- }
- else
- {
- // Take the eye position and direction
- vecAttachment = pOwner->EyePosition();
-
- QAngle angles = pOwner->GetAnimEyeAngles();
- AngleVectors( angles, &vecDir );
- }
-
- trace_t tr;
- UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * MAX_TRACE_LENGTH ), MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
-
- // Backup off the hit plane
- endPos = tr.endpos + ( tr.plane.normal * 4.0f );
- }
- else
- {
- // Just use our position if we can't predict it otherwise
- endPos = GetAbsOrigin();
- }
-
- // Randomly flutter
- scale = 16.0f + random->RandomFloat( -4.0f, 4.0f );
-
- // Draw our laser dot in space
- CMatRenderContextPtr pRenderContext( materials );
- pRenderContext->Bind( m_hSpriteMaterial, this );
- DrawSprite( endPos, scale, scale, color );
-
- return 1;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Setup our sprite reference
-//-----------------------------------------------------------------------------
-void CLaserDot::OnDataChanged( DataUpdateType_t updateType )
-{
- if ( updateType == DATA_UPDATE_CREATED )
- {
- m_hSpriteMaterial.Init( RPG_LASER_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
- }
-}
-
-#endif //CLIENT_DLL
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+#include "weapon_rpg.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+ #include "model_types.h"
+ #include "beamdraw.h"
+ #include "fx_line.h"
+ #include "view.h"
+#else
+ #include "basecombatcharacter.h"
+ #include "movie_explosion.h"
+ #include "soundent.h"
+ #include "player.h"
+ #include "rope.h"
+ #include "vstdlib/random.h"
+ #include "engine/IEngineSound.h"
+ #include "explode.h"
+ #include "util.h"
+ #include "in_buttons.h"
+ #include "shake.h"
+ #include "te_effect_dispatch.h"
+ #include "triggers.h"
+ #include "smoke_trail.h"
+ #include "collisionutils.h"
+ #include "hl2_shareddefs.h"
+#endif
+
+#include "debugoverlay_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define RPG_SPEED 1500
+
+#ifndef CLIENT_DLL
+const char *g_pLaserDotThink = "LaserThinkContext";
+
+static ConVar sk_apc_missile_damage("sk_apc_missile_damage", "15");
+#define APC_MISSILE_DAMAGE sk_apc_missile_damage.GetFloat()
+
+#endif
+
+#ifdef CLIENT_DLL
+#define CLaserDot C_LaserDot
+#endif
+
+//-----------------------------------------------------------------------------
+// Laser Dot
+//-----------------------------------------------------------------------------
+class CLaserDot : public CBaseEntity
+{
+ DECLARE_CLASS( CLaserDot, CBaseEntity );
+public:
+
+ CLaserDot( void );
+ ~CLaserDot( void );
+
+ static CLaserDot *Create( const Vector &origin, CBaseEntity *pOwner = NULL, bool bVisibleDot = true );
+
+ void SetTargetEntity( CBaseEntity *pTarget ) { m_hTargetEnt = pTarget; }
+ CBaseEntity *GetTargetEntity( void ) { return m_hTargetEnt; }
+
+ void SetLaserPosition( const Vector &origin, const Vector &normal );
+ Vector GetChasePosition();
+ void TurnOn( void );
+ void TurnOff( void );
+ bool IsOn() const { return m_bIsOn; }
+
+ void Toggle( void );
+
+ int ObjectCaps() { return (BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION) | FCAP_DONT_SAVE; }
+
+ void MakeInvisible( void );
+
+#ifdef CLIENT_DLL
+
+ virtual bool IsTransparent( void ) { return true; }
+ virtual RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TRANSLUCENT_ENTITY; }
+ virtual int DrawModel( int flags );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual bool ShouldDraw( void ) { return (IsEffectActive(EF_NODRAW)==false); }
+
+ CMaterialReference m_hSpriteMaterial;
+#endif
+
+protected:
+ Vector m_vecSurfaceNormal;
+ EHANDLE m_hTargetEnt;
+ bool m_bVisibleLaserDot;
+ bool m_bIsOn;
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_DATADESC();
+public:
+ CLaserDot *m_pNext;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( LaserDot, DT_LaserDot )
+
+BEGIN_NETWORK_TABLE( CLaserDot, DT_LaserDot )
+END_NETWORK_TABLE()
+
+#ifndef CLIENT_DLL
+
+// a list of laser dots to search quickly
+CEntityClassList<CLaserDot> g_LaserDotList;
+template <> CLaserDot *CEntityClassList<CLaserDot>::m_pClassList = NULL;
+CLaserDot *GetLaserDotList()
+{
+ return g_LaserDotList.m_pClassList;
+}
+
+BEGIN_DATADESC( CMissile )
+
+ DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_hRocketTrail, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_flAugerTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flMarkDeadTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flGracePeriodEndsAt, FIELD_TIME ),
+ DEFINE_FIELD( m_flDamage, FIELD_FLOAT ),
+
+ // Function Pointers
+ DEFINE_FUNCTION( MissileTouch ),
+ DEFINE_FUNCTION( AccelerateThink ),
+ DEFINE_FUNCTION( AugerThink ),
+ DEFINE_FUNCTION( IgniteThink ),
+ DEFINE_FUNCTION( SeekThink ),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( rpg_missile, CMissile );
+
+class CWeaponRPG;
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CMissile::CMissile()
+{
+ m_hRocketTrail = NULL;
+}
+
+CMissile::~CMissile()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+//
+//-----------------------------------------------------------------------------
+void CMissile::Precache( void )
+{
+ PrecacheModel( "models/weapons/w_missile.mdl" );
+ PrecacheModel( "models/weapons/w_missile_launch.mdl" );
+ PrecacheModel( "models/weapons/w_missile_closed.mdl" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+//
+//-----------------------------------------------------------------------------
+void CMissile::Spawn( void )
+{
+ Precache();
+
+ SetSolid( SOLID_BBOX );
+ SetModel("models/weapons/w_missile_launch.mdl");
+ UTIL_SetSize( this, -Vector(4,4,4), Vector(4,4,4) );
+
+ SetTouch( &CMissile::MissileTouch );
+
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
+ SetThink( &CMissile::IgniteThink );
+
+ SetNextThink( gpGlobals->curtime + 0.3f );
+
+ m_takedamage = DAMAGE_YES;
+ m_iHealth = m_iMaxHealth = 100;
+ m_bloodColor = DONT_BLEED;
+ m_flGracePeriodEndsAt = 0;
+
+ AddFlag( FL_OBJECT );
+}
+
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CMissile::Event_Killed( const CTakeDamageInfo &info )
+{
+ m_takedamage = DAMAGE_NO;
+
+ ShotDown();
+}
+
+unsigned int CMissile::PhysicsSolidMaskForEntity( void ) const
+{
+ return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX;
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+int CMissile::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+ if ( ( info.GetDamageType() & (DMG_MISSILEDEFENSE | DMG_AIRBOAT) ) == false )
+ return 0;
+
+ bool bIsDamaged;
+ if( m_iHealth <= AugerHealth() )
+ {
+ // This missile is already damaged (i.e., already running AugerThink)
+ bIsDamaged = true;
+ }
+ else
+ {
+ // This missile isn't damaged enough to wobble in flight yet
+ bIsDamaged = false;
+ }
+
+ int nRetVal = BaseClass::OnTakeDamage_Alive( info );
+
+ if( !bIsDamaged )
+ {
+ if ( m_iHealth <= AugerHealth() )
+ {
+ ShotDown();
+ }
+ }
+
+ return nRetVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Stops any kind of tracking and shoots dumb
+//-----------------------------------------------------------------------------
+void CMissile::DumbFire( void )
+{
+ SetThink( NULL );
+ SetMoveType( MOVETYPE_FLY );
+
+ SetModel("models/weapons/w_missile.mdl");
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+
+ EmitSound( "Missile.Ignite" );
+
+ // Smoke trail.
+ CreateSmokeTrail();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::SetGracePeriod( float flGracePeriod )
+{
+ m_flGracePeriodEndsAt = gpGlobals->curtime + flGracePeriod;
+
+ // Go non-solid until the grace period ends
+ AddSolidFlags( FSOLID_NOT_SOLID );
+}
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CMissile::AccelerateThink( void )
+{
+ Vector vecForward;
+
+ // !!!UNDONE - make this work exactly the same as HL1 RPG, lest we have looping sound bugs again!
+ EmitSound( "Missile.Accelerate" );
+
+ // SetEffects( EF_LIGHT );
+
+ AngleVectors( GetLocalAngles(), &vecForward );
+ SetAbsVelocity( vecForward * RPG_SPEED );
+
+ SetThink( &CMissile::SeekThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+#define AUGER_YDEVIANCE 20.0f
+#define AUGER_XDEVIANCEUP 8.0f
+#define AUGER_XDEVIANCEDOWN 1.0f
+
+//---------------------------------------------------------
+//---------------------------------------------------------
+void CMissile::AugerThink( void )
+{
+ // If we've augered long enough, then just explode
+ if ( m_flAugerTime < gpGlobals->curtime )
+ {
+ Explode();
+ return;
+ }
+
+ if ( m_flMarkDeadTime < gpGlobals->curtime )
+ {
+ m_lifeState = LIFE_DYING;
+ }
+
+ QAngle angles = GetLocalAngles();
+
+ angles.y += random->RandomFloat( -AUGER_YDEVIANCE, AUGER_YDEVIANCE );
+ angles.x += random->RandomFloat( -AUGER_XDEVIANCEDOWN, AUGER_XDEVIANCEUP );
+
+ SetLocalAngles( angles );
+
+ Vector vecForward;
+
+ AngleVectors( GetLocalAngles(), &vecForward );
+
+ SetAbsVelocity( vecForward * 1000.0f );
+
+ SetNextThink( gpGlobals->curtime + 0.05f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Causes the missile to spiral to the ground and explode, due to damage
+//-----------------------------------------------------------------------------
+void CMissile::ShotDown( void )
+{
+ CEffectData data;
+ data.m_vOrigin = GetAbsOrigin();
+
+ DispatchEffect( "RPGShotDown", data );
+
+ if ( m_hRocketTrail != NULL )
+ {
+ m_hRocketTrail->m_bDamaged = true;
+ }
+
+ SetThink( &CMissile::AugerThink );
+ SetNextThink( gpGlobals->curtime );
+ m_flAugerTime = gpGlobals->curtime + 1.5f;
+ m_flMarkDeadTime = gpGlobals->curtime + 0.75;
+
+ // Let the RPG start reloading immediately
+ if ( m_hOwner != NULL )
+ {
+ m_hOwner->NotifyRocketDied();
+ m_hOwner = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// The actual explosion
+//-----------------------------------------------------------------------------
+void CMissile::DoExplosion( void )
+{
+ // Explode
+ ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(), GetDamage(), GetDamage() * 2,
+ SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::Explode( void )
+{
+ // Don't explode against the skybox. Just pretend that
+ // the missile flies off into the distance.
+ Vector forward;
+
+ GetVectors( &forward, NULL, NULL );
+
+ trace_t tr;
+ UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + forward * 16, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+
+ m_takedamage = DAMAGE_NO;
+ SetSolid( SOLID_NONE );
+ if( tr.fraction == 1.0 || !(tr.surface.flags & SURF_SKY) )
+ {
+ DoExplosion();
+ }
+
+ if( m_hRocketTrail )
+ {
+ m_hRocketTrail->SetLifetime(0.1f);
+ m_hRocketTrail = NULL;
+ }
+
+ if ( m_hOwner != NULL )
+ {
+ m_hOwner->NotifyRocketDied();
+ m_hOwner = NULL;
+ }
+
+ StopSound( "Missile.Ignite" );
+ UTIL_Remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CMissile::MissileTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+
+ // Don't touch triggers (but DO hit weapons)
+ if ( pOther->IsSolidFlagSet(FSOLID_TRIGGER|FSOLID_VOLUME_CONTENTS) && pOther->GetCollisionGroup() != COLLISION_GROUP_WEAPON )
+ return;
+
+ Explode();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::CreateSmokeTrail( void )
+{
+ if ( m_hRocketTrail )
+ return;
+
+ // Smoke trail.
+ if ( (m_hRocketTrail = RocketTrail::CreateRocketTrail()) != NULL )
+ {
+ m_hRocketTrail->m_Opacity = 0.2f;
+ m_hRocketTrail->m_SpawnRate = 100;
+ m_hRocketTrail->m_ParticleLifetime = 0.5f;
+ m_hRocketTrail->m_StartColor.Init( 0.65f, 0.65f , 0.65f );
+ m_hRocketTrail->m_EndColor.Init( 0.0, 0.0, 0.0 );
+ m_hRocketTrail->m_StartSize = 8;
+ m_hRocketTrail->m_EndSize = 32;
+ m_hRocketTrail->m_SpawnRadius = 4;
+ m_hRocketTrail->m_MinSpeed = 2;
+ m_hRocketTrail->m_MaxSpeed = 16;
+
+ m_hRocketTrail->SetLifetime( 999 );
+ m_hRocketTrail->FollowEntity( this, "0" );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::IgniteThink( void )
+{
+ SetMoveType( MOVETYPE_FLY );
+ SetModel("models/weapons/w_missile.mdl");
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+
+ //TODO: Play opening sound
+
+ Vector vecForward;
+
+ EmitSound( "Missile.Ignite" );
+
+ AngleVectors( GetLocalAngles(), &vecForward );
+ SetAbsVelocity( vecForward * RPG_SPEED );
+
+ SetThink( &CMissile::SeekThink );
+ SetNextThink( gpGlobals->curtime );
+
+ if ( m_hOwner && m_hOwner->GetOwner() )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( m_hOwner->GetOwner() );
+
+ color32 white = { 255,225,205,64 };
+ UTIL_ScreenFade( pPlayer, white, 0.1f, 0.0f, FFADE_IN );
+ }
+
+ CreateSmokeTrail();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the shooting position
+//-----------------------------------------------------------------------------
+void CMissile::GetShootPosition( CLaserDot *pLaserDot, Vector *pShootPosition )
+{
+ if ( pLaserDot->GetOwnerEntity() != NULL )
+ {
+ //FIXME: Do we care this isn't exactly the muzzle position?
+ *pShootPosition = pLaserDot->GetOwnerEntity()->WorldSpaceCenter();
+ }
+ else
+ {
+ *pShootPosition = pLaserDot->GetChasePosition();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#define RPG_HOMING_SPEED 0.125f
+
+void CMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed )
+{
+ *pHomingSpeed = RPG_HOMING_SPEED;
+ if ( pLaserDot->GetTargetEntity() )
+ {
+ *pActualDotPosition = pLaserDot->GetChasePosition();
+ return;
+ }
+
+ Vector vLaserStart;
+ GetShootPosition( pLaserDot, &vLaserStart );
+
+ //Get the laser's vector
+ Vector vLaserDir;
+ VectorSubtract( pLaserDot->GetChasePosition(), vLaserStart, vLaserDir );
+
+ //Find the length of the current laser
+ float flLaserLength = VectorNormalize( vLaserDir );
+
+ //Find the length from the missile to the laser's owner
+ float flMissileLength = GetAbsOrigin().DistTo( vLaserStart );
+
+ //Find the length from the missile to the laser's position
+ Vector vecTargetToMissile;
+ VectorSubtract( GetAbsOrigin(), pLaserDot->GetChasePosition(), vecTargetToMissile );
+ float flTargetLength = VectorNormalize( vecTargetToMissile );
+
+ // See if we should chase the line segment nearest us
+ if ( ( flMissileLength < flLaserLength ) || ( flTargetLength <= 512.0f ) )
+ {
+ *pActualDotPosition = UTIL_PointOnLineNearestPoint( vLaserStart, pLaserDot->GetChasePosition(), GetAbsOrigin() );
+ *pActualDotPosition += ( vLaserDir * 256.0f );
+ }
+ else
+ {
+ // Otherwise chase the dot
+ *pActualDotPosition = pLaserDot->GetChasePosition();
+ }
+
+// NDebugOverlay::Line( pLaserDot->GetChasePosition(), vLaserStart, 0, 255, 0, true, 0.05f );
+// NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );
+// NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CMissile::SeekThink( void )
+{
+ CBaseEntity *pBestDot = NULL;
+ float flBestDist = MAX_TRACE_LENGTH;
+ float dotDist;
+
+ // If we have a grace period, go solid when it ends
+ if ( m_flGracePeriodEndsAt )
+ {
+ if ( m_flGracePeriodEndsAt < gpGlobals->curtime )
+ {
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+ m_flGracePeriodEndsAt = 0;
+ }
+ }
+
+ //Search for all dots relevant to us
+ for( CLaserDot *pEnt = GetLaserDotList(); pEnt != NULL; pEnt = pEnt->m_pNext )
+ {
+ if ( !pEnt->IsOn() )
+ continue;
+
+ if ( pEnt->GetOwnerEntity() != GetOwnerEntity() )
+ continue;
+
+ dotDist = (GetAbsOrigin() - pEnt->GetAbsOrigin()).Length();
+
+ //Find closest
+ if ( dotDist < flBestDist )
+ {
+ pBestDot = pEnt;
+ flBestDist = dotDist;
+ }
+ }
+
+ //If we have a dot target
+ if ( pBestDot == NULL )
+ {
+ //Think as soon as possible
+ SetNextThink( gpGlobals->curtime );
+ return;
+ }
+
+ CLaserDot *pLaserDot = (CLaserDot *)pBestDot;
+ Vector targetPos;
+
+ float flHomingSpeed;
+ Vector vecLaserDotPosition;
+ ComputeActualDotPosition( pLaserDot, &targetPos, &flHomingSpeed );
+
+ if ( IsSimulatingOnAlternateTicks() )
+ flHomingSpeed *= 2;
+
+ Vector vTargetDir;
+ VectorSubtract( targetPos, GetAbsOrigin(), vTargetDir );
+ float flDist = VectorNormalize( vTargetDir );
+
+ Vector vDir = GetAbsVelocity();
+ float flSpeed = VectorNormalize( vDir );
+ Vector vNewVelocity = vDir;
+ if ( gpGlobals->frametime > 0.0f )
+ {
+ if ( flSpeed != 0 )
+ {
+ vNewVelocity = ( flHomingSpeed * vTargetDir ) + ( ( 1 - flHomingSpeed ) * vDir );
+
+ // This computation may happen to cancel itself out exactly. If so, slam to targetdir.
+ if ( VectorNormalize( vNewVelocity ) < 1e-3 )
+ {
+ vNewVelocity = (flDist != 0) ? vTargetDir : vDir;
+ }
+ }
+ else
+ {
+ vNewVelocity = vTargetDir;
+ }
+ }
+
+ QAngle finalAngles;
+ VectorAngles( vNewVelocity, finalAngles );
+ SetAbsAngles( finalAngles );
+
+ vNewVelocity *= flSpeed;
+ SetAbsVelocity( vNewVelocity );
+
+ if( GetAbsVelocity() == vec3_origin )
+ {
+ // Strange circumstances have brought this missile to halt. Just blow it up.
+ Explode();
+ return;
+ }
+
+ // Think as soon as possible
+ SetNextThink( gpGlobals->curtime );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : &vecOrigin -
+// &vecAngles -
+// NULL -
+//
+// Output : CMissile
+//-----------------------------------------------------------------------------
+CMissile *CMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner = NULL )
+{
+ //CMissile *pMissile = (CMissile *)CreateEntityByName("rpg_missile" );
+ CMissile *pMissile = (CMissile *) CBaseEntity::Create( "rpg_missile", vecOrigin, vecAngles, CBaseEntity::Instance( pentOwner ) );
+ pMissile->SetOwnerEntity( Instance( pentOwner ) );
+ pMissile->Spawn();
+ pMissile->AddEffects( EF_NOSHADOW );
+
+ Vector vecForward;
+ AngleVectors( vecAngles, &vecForward );
+
+ pMissile->SetAbsVelocity( vecForward * 300 + Vector( 0,0, 128 ) );
+
+ return pMissile;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// This entity is used to create little force boxes that the helicopter
+// should avoid.
+//-----------------------------------------------------------------------------
+class CInfoAPCMissileHint : public CBaseEntity
+{
+ DECLARE_DATADESC();
+
+public:
+ DECLARE_CLASS( CInfoAPCMissileHint, CBaseEntity );
+
+ virtual void Spawn( );
+ virtual void Activate();
+ virtual void UpdateOnRemove();
+
+ static CBaseEntity *FindAimTarget( CBaseEntity *pMissile, const char *pTargetName,
+ const Vector &vecCurrentTargetPos, const Vector &vecCurrentTargetVel );
+
+private:
+ EHANDLE m_hTarget;
+
+ typedef CHandle<CInfoAPCMissileHint> APCMissileHintHandle_t;
+ static CUtlVector< APCMissileHintHandle_t > s_APCMissileHints;
+};
+
+
+//-----------------------------------------------------------------------------
+//
+// This entity is used to create little force boxes that the helicopters should avoid.
+//
+//-----------------------------------------------------------------------------
+CUtlVector< CInfoAPCMissileHint::APCMissileHintHandle_t > CInfoAPCMissileHint::s_APCMissileHints;
+
+LINK_ENTITY_TO_CLASS( info_apc_missile_hint, CInfoAPCMissileHint );
+
+BEGIN_DATADESC( CInfoAPCMissileHint )
+ DEFINE_FIELD( m_hTarget, FIELD_EHANDLE ),
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Spawn, remove
+//-----------------------------------------------------------------------------
+void CInfoAPCMissileHint::Spawn( )
+{
+ SetModel( STRING( GetModelName() ) );
+ SetSolid( SOLID_BSP );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ AddEffects( EF_NODRAW );
+}
+
+void CInfoAPCMissileHint::Activate( )
+{
+ BaseClass::Activate();
+
+ m_hTarget = gEntList.FindEntityByName( NULL, m_target );
+ if ( m_hTarget == NULL )
+ {
+ DevWarning( "%s: Could not find target '%s'!\n", GetClassname(), STRING( m_target ) );
+ }
+ else
+ {
+ s_APCMissileHints.AddToTail( this );
+ }
+}
+
+void CInfoAPCMissileHint::UpdateOnRemove( )
+{
+ s_APCMissileHints.FindAndRemove( this );
+ BaseClass::UpdateOnRemove();
+}
+
+
+//-----------------------------------------------------------------------------
+// Where are how should we avoid?
+//-----------------------------------------------------------------------------
+#define HINT_PREDICTION_TIME 3.0f
+
+CBaseEntity *CInfoAPCMissileHint::FindAimTarget( CBaseEntity *pMissile, const char *pTargetName,
+ const Vector &vecCurrentEnemyPos, const Vector &vecCurrentEnemyVel )
+{
+ if ( !pTargetName )
+ return NULL;
+
+ float flOOSpeed = pMissile->GetAbsVelocity().Length();
+ if ( flOOSpeed != 0.0f )
+ {
+ flOOSpeed = 1.0f / flOOSpeed;
+ }
+
+ for ( int i = s_APCMissileHints.Count(); --i >= 0; )
+ {
+ CInfoAPCMissileHint *pHint = s_APCMissileHints[i];
+ if ( !pHint->NameMatches( pTargetName ) )
+ continue;
+
+ if ( !pHint->m_hTarget )
+ continue;
+
+ Vector vecMissileToHint, vecMissileToEnemy;
+ VectorSubtract( pHint->m_hTarget->WorldSpaceCenter(), pMissile->GetAbsOrigin(), vecMissileToHint );
+ VectorSubtract( vecCurrentEnemyPos, pMissile->GetAbsOrigin(), vecMissileToEnemy );
+ float flDistMissileToHint = VectorNormalize( vecMissileToHint );
+ VectorNormalize( vecMissileToEnemy );
+ if ( DotProduct( vecMissileToHint, vecMissileToEnemy ) < 0.866f )
+ continue;
+
+ // Determine when the target will be inside the volume.
+ // Project at most 3 seconds in advance
+ Vector vecRayDelta;
+ VectorMultiply( vecCurrentEnemyVel, HINT_PREDICTION_TIME, vecRayDelta );
+
+ BoxTraceInfo_t trace;
+ if ( !IntersectRayWithOBB( vecCurrentEnemyPos, vecRayDelta, pHint->CollisionProp()->CollisionToWorldTransform(),
+ pHint->CollisionProp()->OBBMins(), pHint->CollisionProp()->OBBMaxs(), 0.0f, &trace ))
+ {
+ continue;
+ }
+
+ // Determine the amount of time it would take the missile to reach the target
+ // If we can reach the target within the time it takes for the enemy to reach the
+ float tSqr = flDistMissileToHint * flOOSpeed / HINT_PREDICTION_TIME;
+ if ( (tSqr < (trace.t1 * trace.t1)) || (tSqr > (trace.t2 * trace.t2)) )
+ continue;
+
+ return pHint->m_hTarget;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// a list of missiles to search quickly
+//-----------------------------------------------------------------------------
+CEntityClassList<CAPCMissile> g_APCMissileList;
+template <> CAPCMissile *CEntityClassList<CAPCMissile>::m_pClassList = NULL;
+CAPCMissile *GetAPCMissileList()
+{
+ return g_APCMissileList.m_pClassList;
+}
+
+//-----------------------------------------------------------------------------
+// Finds apc missiles in cone
+//-----------------------------------------------------------------------------
+CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDirection, float flAngle )
+{
+ float flCosAngle = cos( DEG2RAD( flAngle ) );
+ for( CAPCMissile *pEnt = GetAPCMissileList(); pEnt != NULL; pEnt = pEnt->m_pNext )
+ {
+ if ( !pEnt->IsSolid() )
+ continue;
+
+ Vector vecDelta;
+ VectorSubtract( pEnt->GetAbsOrigin(), vecOrigin, vecDelta );
+ VectorNormalize( vecDelta );
+ float flDot = DotProduct( vecDelta, vecDirection );
+ if ( flDot > flCosAngle )
+ return pEnt;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Specialized version of the missile
+//
+//-----------------------------------------------------------------------------
+#define MAX_HOMING_DISTANCE 2250.0f
+#define MIN_HOMING_DISTANCE 1250.0f
+#define MAX_NEAR_HOMING_DISTANCE 1750.0f
+#define MIN_NEAR_HOMING_DISTANCE 1000.0f
+#define DOWNWARD_BLEND_TIME_START 0.2f
+#define MIN_HEIGHT_DIFFERENCE 250.0f
+#define MAX_HEIGHT_DIFFERENCE 550.0f
+#define CORRECTION_TIME 0.2f
+#define APC_LAUNCH_HOMING_SPEED 0.1f
+#define APC_HOMING_SPEED 0.025f
+#define HOMING_SPEED_ACCEL 0.01f
+
+BEGIN_DATADESC( CAPCMissile )
+
+ DEFINE_FIELD( m_flReachedTargetTime, FIELD_TIME ),
+ DEFINE_FIELD( m_flIgnitionTime, FIELD_TIME ),
+ DEFINE_FIELD( m_bGuidingDisabled, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_hSpecificTarget, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_strHint, FIELD_STRING ),
+ DEFINE_FIELD( m_flLastHomingSpeed, FIELD_FLOAT ),
+// DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ),
+
+ DEFINE_THINKFUNC( BeginSeekThink ),
+ DEFINE_THINKFUNC( AugerStartThink ),
+ DEFINE_THINKFUNC( ExplodeThink ),
+
+ DEFINE_FUNCTION( APCMissileTouch ),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( apc_missile, CAPCMissile );
+
+CAPCMissile *CAPCMissile::Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, CBaseEntity *pOwner )
+{
+ CAPCMissile *pMissile = (CAPCMissile *)CBaseEntity::Create( "apc_missile", vecOrigin, vecAngles, pOwner );
+ pMissile->SetOwnerEntity( pOwner );
+ pMissile->Spawn();
+ pMissile->SetAbsVelocity( vecVelocity );
+ pMissile->AddFlag( FL_NOTARGET );
+ pMissile->AddEffects( EF_NOSHADOW );
+ return pMissile;
+}
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CAPCMissile::CAPCMissile()
+{
+ g_APCMissileList.Insert( this );
+}
+
+CAPCMissile::~CAPCMissile()
+{
+ g_APCMissileList.Remove( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Shared initialization code
+//-----------------------------------------------------------------------------
+void CAPCMissile::Init()
+{
+ SetMoveType( MOVETYPE_FLY );
+ SetModel("models/weapons/w_missile.mdl");
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+ CreateSmokeTrail();
+ SetTouch( &CAPCMissile::APCMissileTouch );
+ m_flLastHomingSpeed = APC_HOMING_SPEED;
+}
+
+
+//-----------------------------------------------------------------------------
+// For hitting a specific target
+//-----------------------------------------------------------------------------
+void CAPCMissile::AimAtSpecificTarget( CBaseEntity *pTarget )
+{
+ m_hSpecificTarget = pTarget;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CAPCMissile::APCMissileTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+ if ( !pOther->IsSolid() && !pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
+ return;
+
+ Explode();
+}
+
+
+//-----------------------------------------------------------------------------
+// Specialized version of the missile
+//-----------------------------------------------------------------------------
+void CAPCMissile::IgniteDelay( void )
+{
+ m_flIgnitionTime = gpGlobals->curtime + 0.3f;
+
+ SetThink( &CAPCMissile::BeginSeekThink );
+ SetNextThink( m_flIgnitionTime );
+ Init();
+ AddSolidFlags( FSOLID_NOT_SOLID );
+}
+
+void CAPCMissile::AugerDelay( float flDelay )
+{
+ m_flIgnitionTime = gpGlobals->curtime;
+ SetThink( &CAPCMissile::AugerStartThink );
+ SetNextThink( gpGlobals->curtime + flDelay );
+ Init();
+ DisableGuiding();
+}
+
+void CAPCMissile::AugerStartThink()
+{
+ if ( m_hRocketTrail != NULL )
+ {
+ m_hRocketTrail->m_bDamaged = true;
+ }
+ m_flAugerTime = gpGlobals->curtime + random->RandomFloat( 1.0f, 2.0f );
+ SetThink( &CAPCMissile::AugerThink );
+ SetNextThink( gpGlobals->curtime );
+}
+
+void CAPCMissile::ExplodeDelay( float flDelay )
+{
+ m_flIgnitionTime = gpGlobals->curtime;
+ SetThink( &CAPCMissile::ExplodeThink );
+ SetNextThink( gpGlobals->curtime + flDelay );
+ Init();
+ DisableGuiding();
+}
+
+
+void CAPCMissile::BeginSeekThink( void )
+{
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+ SetThink( &CAPCMissile::SeekThink );
+ SetNextThink( gpGlobals->curtime );
+}
+
+void CAPCMissile::ExplodeThink()
+{
+ DoExplosion();
+}
+
+//-----------------------------------------------------------------------------
+// Health lost at which augering starts
+//-----------------------------------------------------------------------------
+int CAPCMissile::AugerHealth()
+{
+ return m_iMaxHealth - 25;
+}
+
+
+//-----------------------------------------------------------------------------
+// Health lost at which augering starts
+//-----------------------------------------------------------------------------
+void CAPCMissile::DisableGuiding()
+{
+ m_bGuidingDisabled = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Guidance hints
+//-----------------------------------------------------------------------------
+void CAPCMissile::SetGuidanceHint( const char *pHintName )
+{
+ m_strHint = MAKE_STRING( pHintName );
+}
+
+
+//-----------------------------------------------------------------------------
+// The actual explosion
+//-----------------------------------------------------------------------------
+void CAPCMissile::DoExplosion( void )
+{
+ if ( GetWaterLevel() != 0 )
+ {
+ CEffectData data;
+ data.m_vOrigin = WorldSpaceCenter();
+ data.m_flMagnitude = 128;
+ data.m_flScale = 128;
+ data.m_fFlags = 0;
+ DispatchEffect( "WaterSurfaceExplosion", data );
+ }
+ else
+ {
+ ExplosionCreate( GetAbsOrigin(), GetAbsAngles(), GetOwnerEntity(),
+ APC_MISSILE_DAMAGE, 100, true, 20000 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAPCMissile::ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition )
+{
+ Vector vecTarget = pTarget->BodyTarget( vecShootPosition, false );
+ float flShotSpeed = GetAbsVelocity().Length();
+ if ( flShotSpeed == 0 )
+ {
+ *pLeadPosition = vecTarget;
+ return;
+ }
+
+ Vector vecVelocity = pTarget->GetSmoothedVelocity();
+ vecVelocity.z = 0.0f;
+ float flTargetSpeed = VectorNormalize( vecVelocity );
+ Vector vecDelta;
+ VectorSubtract( vecShootPosition, vecTarget, vecDelta );
+ float flTargetToShooter = VectorNormalize( vecDelta );
+ float flCosTheta = DotProduct( vecDelta, vecVelocity );
+
+ // Law of cosines... z^2 = x^2 + y^2 - 2xy cos Theta
+ // where z = flShooterToPredictedTargetPosition = flShotSpeed * predicted time
+ // x = flTargetSpeed * predicted time
+ // y = flTargetToShooter
+ // solve for predicted time using at^2 + bt + c = 0, t = (-b +/- sqrt( b^2 - 4ac )) / 2a
+ float a = flTargetSpeed * flTargetSpeed - flShotSpeed * flShotSpeed;
+ float b = -2.0f * flTargetToShooter * flCosTheta * flTargetSpeed;
+ float c = flTargetToShooter * flTargetToShooter;
+
+ float flDiscrim = b*b - 4*a*c;
+ if (flDiscrim < 0)
+ {
+ *pLeadPosition = vecTarget;
+ return;
+ }
+
+ flDiscrim = sqrt(flDiscrim);
+ float t = (-b + flDiscrim) / (2.0f * a);
+ float t2 = (-b - flDiscrim) / (2.0f * a);
+ if ( t < t2 )
+ {
+ t = t2;
+ }
+
+ if ( t <= 0.0f )
+ {
+ *pLeadPosition = vecTarget;
+ return;
+ }
+
+ VectorMA( vecTarget, flTargetSpeed * t, vecVelocity, *pLeadPosition );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAPCMissile::ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed )
+{
+ if ( m_bGuidingDisabled )
+ {
+ *pActualDotPosition = GetAbsOrigin();
+ *pHomingSpeed = 0.0f;
+ m_flLastHomingSpeed = *pHomingSpeed;
+ return;
+ }
+
+ if ( ( m_strHint != NULL_STRING ) && (!m_hSpecificTarget) )
+ {
+ Vector vecOrigin, vecVelocity;
+ CBaseEntity *pTarget = pLaserDot->GetTargetEntity();
+ if ( pTarget )
+ {
+ vecOrigin = pTarget->BodyTarget( GetAbsOrigin(), false );
+ vecVelocity = pTarget->GetSmoothedVelocity();
+ }
+ else
+ {
+ vecOrigin = pLaserDot->GetChasePosition();
+ vecVelocity = vec3_origin;
+ }
+
+ m_hSpecificTarget = CInfoAPCMissileHint::FindAimTarget( this, STRING( m_strHint ), vecOrigin, vecVelocity );
+ }
+
+ CBaseEntity *pLaserTarget = m_hSpecificTarget ? m_hSpecificTarget.Get() : pLaserDot->GetTargetEntity();
+ if ( !pLaserTarget )
+ {
+ BaseClass::ComputeActualDotPosition( pLaserDot, pActualDotPosition, pHomingSpeed );
+ m_flLastHomingSpeed = *pHomingSpeed;
+ return;
+ }
+
+ if ( pLaserTarget->ClassMatches( "npc_bullseye" ) )
+ {
+ if ( m_flLastHomingSpeed != RPG_HOMING_SPEED )
+ {
+ if (m_flLastHomingSpeed > RPG_HOMING_SPEED)
+ {
+ m_flLastHomingSpeed -= HOMING_SPEED_ACCEL * UTIL_GetSimulationInterval();
+ if ( m_flLastHomingSpeed < RPG_HOMING_SPEED )
+ {
+ m_flLastHomingSpeed = RPG_HOMING_SPEED;
+ }
+ }
+ else
+ {
+ m_flLastHomingSpeed += HOMING_SPEED_ACCEL * UTIL_GetSimulationInterval();
+ if ( m_flLastHomingSpeed > RPG_HOMING_SPEED )
+ {
+ m_flLastHomingSpeed = RPG_HOMING_SPEED;
+ }
+ }
+ }
+ *pHomingSpeed = m_flLastHomingSpeed;
+ *pActualDotPosition = pLaserTarget->WorldSpaceCenter();
+ return;
+ }
+
+ Vector vLaserStart;
+ GetShootPosition( pLaserDot, &vLaserStart );
+ *pHomingSpeed = APC_LAUNCH_HOMING_SPEED;
+
+ //Get the laser's vector
+ Vector vecTargetPosition = pLaserTarget->BodyTarget( GetAbsOrigin(), false );
+
+ // Compute leading position
+ Vector vecLeadPosition;
+ ComputeLeadingPosition( GetAbsOrigin(), pLaserTarget, &vecLeadPosition );
+
+ Vector vecTargetToMissile, vecTargetToShooter;
+ VectorSubtract( GetAbsOrigin(), vecTargetPosition, vecTargetToMissile );
+ VectorSubtract( vLaserStart, vecTargetPosition, vecTargetToShooter );
+
+ *pActualDotPosition = vecLeadPosition;
+
+ float flMinHomingDistance = MIN_HOMING_DISTANCE;
+ float flMaxHomingDistance = MAX_HOMING_DISTANCE;
+ float flBlendTime = gpGlobals->curtime - m_flIgnitionTime;
+ if ( flBlendTime > DOWNWARD_BLEND_TIME_START )
+ {
+ if ( m_flReachedTargetTime != 0.0f )
+ {
+ *pHomingSpeed = APC_HOMING_SPEED;
+ float flDeltaTime = clamp( gpGlobals->curtime - m_flReachedTargetTime, 0.0f, CORRECTION_TIME );
+ *pHomingSpeed = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, 0.2f, *pHomingSpeed );
+ flMinHomingDistance = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, MIN_NEAR_HOMING_DISTANCE, flMinHomingDistance );
+ flMaxHomingDistance = SimpleSplineRemapVal( flDeltaTime, 0.0f, CORRECTION_TIME, MAX_NEAR_HOMING_DISTANCE, flMaxHomingDistance );
+ }
+ else
+ {
+ flMinHomingDistance = MIN_NEAR_HOMING_DISTANCE;
+ flMaxHomingDistance = MAX_NEAR_HOMING_DISTANCE;
+ Vector vecDelta;
+ VectorSubtract( GetAbsOrigin(), *pActualDotPosition, vecDelta );
+ if ( vecDelta.z > MIN_HEIGHT_DIFFERENCE )
+ {
+ float flClampedHeight = clamp( vecDelta.z, MIN_HEIGHT_DIFFERENCE, MAX_HEIGHT_DIFFERENCE );
+ float flHeightAdjustFactor = SimpleSplineRemapVal( flClampedHeight, MIN_HEIGHT_DIFFERENCE, MAX_HEIGHT_DIFFERENCE, 0.0f, 1.0f );
+
+ vecDelta.z = 0.0f;
+ float flDist = VectorNormalize( vecDelta );
+
+ float flForwardOffset = 2000.0f;
+ if ( flDist > flForwardOffset )
+ {
+ Vector vecNewPosition;
+ VectorMA( GetAbsOrigin(), -flForwardOffset, vecDelta, vecNewPosition );
+ vecNewPosition.z = pActualDotPosition->z;
+
+ VectorLerp( *pActualDotPosition, vecNewPosition, flHeightAdjustFactor, *pActualDotPosition );
+ }
+ }
+ else
+ {
+ m_flReachedTargetTime = gpGlobals->curtime;
+ }
+ }
+
+ // Allows for players right at the edge of rocket range to be threatened
+ if ( flBlendTime > 0.6f )
+ {
+ float flTargetLength = GetAbsOrigin().DistTo( pLaserTarget->WorldSpaceCenter() );
+ flTargetLength = clamp( flTargetLength, flMinHomingDistance, flMaxHomingDistance );
+ *pHomingSpeed = SimpleSplineRemapVal( flTargetLength, flMaxHomingDistance, flMinHomingDistance, *pHomingSpeed, 0.01f );
+ }
+ }
+
+ float flDot = DotProduct2D( vecTargetToShooter.AsVector2D(), vecTargetToMissile.AsVector2D() );
+ if ( ( flDot < 0 ) || m_bGuidingDisabled )
+ {
+ *pHomingSpeed = 0.0f;
+ }
+
+ m_flLastHomingSpeed = *pHomingSpeed;
+
+// NDebugOverlay::Line( vecLeadPosition, GetAbsOrigin(), 0, 255, 0, true, 0.05f );
+// NDebugOverlay::Line( GetAbsOrigin(), *pActualDotPosition, 255, 0, 0, true, 0.05f );
+// NDebugOverlay::Cross3D( *pActualDotPosition, -Vector(4,4,4), Vector(4,4,4), 255, 0, 0, true, 0.05f );
+}
+
+#endif
+
+#define RPG_BEAM_SPRITE "effects/laser1.vmt"
+#define RPG_BEAM_SPRITE_NOZ "effects/laser1_noz.vmt"
+#define RPG_LASER_SPRITE "sprites/redglow1"
+
+//=============================================================================
+// RPG
+//=============================================================================
+
+LINK_ENTITY_TO_CLASS( weapon_rpg, CWeaponRPG );
+PRECACHE_WEAPON_REGISTER(weapon_rpg);
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRPG, DT_WeaponRPG )
+
+#ifdef CLIENT_DLL
+void RecvProxy_MissileDied( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ CWeaponRPG *pRPG = ((CWeaponRPG*)pStruct);
+
+ RecvProxy_IntToEHandle( pData, pStruct, pOut );
+
+ CBaseEntity *pNewMissile = pRPG->GetMissile();
+
+ if ( pNewMissile == NULL )
+ {
+ if ( pRPG->GetOwner() && pRPG->GetOwner()->GetActiveWeapon() == pRPG )
+ {
+ pRPG->NotifyRocketDied();
+ }
+ }
+}
+
+#endif
+
+BEGIN_NETWORK_TABLE( CWeaponRPG, DT_WeaponRPG )
+#ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bInitialStateUpdate ) ),
+ RecvPropBool( RECVINFO( m_bGuiding ) ),
+ RecvPropBool( RECVINFO( m_bHideGuiding ) ),
+ RecvPropEHandle( RECVINFO( m_hMissile ), RecvProxy_MissileDied ),
+ RecvPropVector( RECVINFO( m_vecLaserDot ) ),
+#else
+ SendPropBool( SENDINFO( m_bInitialStateUpdate ) ),
+ SendPropBool( SENDINFO( m_bGuiding ) ),
+ SendPropBool( SENDINFO( m_bHideGuiding ) ),
+ SendPropEHandle( SENDINFO( m_hMissile ) ),
+ SendPropVector( SENDINFO( m_vecLaserDot ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+
+BEGIN_PREDICTION_DATA( CWeaponRPG )
+ DEFINE_PRED_FIELD( m_bInitialStateUpdate, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bGuiding, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bHideGuiding, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+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 },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponRPG);
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponRPG::CWeaponRPG()
+{
+ m_bReloadsSingly = true;
+ m_bInitialStateUpdate= false;
+ m_bHideGuiding = false;
+ m_bGuiding = false;
+
+ m_fMinRange1 = m_fMinRange2 = 40*12;
+ m_fMaxRange1 = m_fMaxRange2 = 500*12;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponRPG::~CWeaponRPG()
+{
+#ifndef CLIENT_DLL
+ if ( m_hLaserDot != NULL )
+ {
+ UTIL_Remove( m_hLaserDot );
+ m_hLaserDot = NULL;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "Missile.Ignite" );
+ PrecacheScriptSound( "Missile.Accelerate" );
+
+ // Laser dot...
+ PrecacheModel( "sprites/redglow1.vmt" );
+ PrecacheModel( RPG_LASER_SPRITE );
+ PrecacheModel( RPG_BEAM_SPRITE );
+ PrecacheModel( RPG_BEAM_SPRITE_NOZ );
+
+#ifndef CLIENT_DLL
+ UTIL_PrecacheOther( "rpg_missile" );
+#endif
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::Activate( void )
+{
+ BaseClass::Activate();
+
+ // Restore the laser pointer after transition
+ if ( m_bGuiding )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( pOwner->GetActiveWeapon() == this )
+ {
+ StartGuiding();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::HasAnyAmmo( void )
+{
+ if ( m_hMissile != NULL )
+ return true;
+
+ return BaseClass::HasAnyAmmo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::WeaponShouldBeLowered( void )
+{
+ // Lower us if we're out of ammo
+ if ( !HasAnyAmmo() )
+ return true;
+
+ return BaseClass::WeaponShouldBeLowered();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+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;
+
+ // Can't be reloading
+ if ( GetActivity() == ACT_VM_RELOAD )
+ return;
+
+ Vector vecOrigin;
+ Vector vecForward;
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner == NULL )
+ return;
+
+ Vector vForward, vRight, vUp;
+
+ pOwner->EyeVectors( &vForward, &vRight, &vUp );
+
+ Vector muzzlePoint = pOwner->Weapon_ShootPosition() + vForward * 12.0f + vRight * 6.0f + vUp * -3.0f;
+
+#ifndef CLIENT_DLL
+ QAngle vecAngles;
+ VectorAngles( vForward, vecAngles );
+
+ CMissile *pMissile = CMissile::Create( muzzlePoint, vecAngles, GetOwner()->edict() );
+ pMissile->m_hOwner = this;
+
+ // If the shot is clear to the player, give the missile a grace period
+ trace_t tr;
+ Vector vecEye = pOwner->EyePosition();
+ UTIL_TraceLine( vecEye, vecEye + vForward * 128, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction == 1.0 )
+ {
+ pMissile->SetGracePeriod( 0.3 );
+ }
+
+ pMissile->SetDamage( GetHL2MPWpnData().m_iPlayerDamage );
+
+ m_hMissile = pMissile;
+#endif
+
+ DecrementAmmo( GetOwner() );
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ WeaponSound( SINGLE );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOwner -
+//-----------------------------------------------------------------------------
+void CWeaponRPG::DecrementAmmo( CBaseCombatCharacter *pOwner )
+{
+ // Take away our primary ammo type
+ pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : state -
+//-----------------------------------------------------------------------------
+void CWeaponRPG::SuppressGuiding( bool state )
+{
+ m_bHideGuiding = state;
+
+#ifndef CLIENT_DLL
+
+ if ( m_hLaserDot == NULL )
+ {
+ StartGuiding();
+
+ //STILL!?
+ if ( m_hLaserDot == NULL )
+ return;
+ }
+
+ if ( state )
+ {
+ m_hLaserDot->TurnOff();
+ }
+ else
+ {
+ m_hLaserDot->TurnOn();
+ }
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override this if we're guiding a missile currently
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Lower( void )
+{
+ if ( m_hMissile != NULL )
+ return false;
+
+ return BaseClass::Lower();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::ItemPostFrame( void )
+{
+ BaseClass::ItemPostFrame();
+
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if ( pPlayer == NULL )
+ return;
+
+ //If we're pulling the weapon out for the first time, wait to draw the laser
+ if ( ( m_bInitialStateUpdate ) && ( GetActivity() != ACT_VM_DRAW ) )
+ {
+ StartGuiding();
+ m_bInitialStateUpdate = false;
+ }
+
+ // Supress our guiding effects if we're lowered
+ if ( GetIdealActivity() == ACT_VM_IDLE_LOWERED )
+ {
+ SuppressGuiding();
+ }
+ else
+ {
+ SuppressGuiding( false );
+ }
+
+ //Move the laser
+ UpdateLaserPosition();
+
+ if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 && m_hMissile == NULL )
+ {
+ StopGuiding();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Vector
+//-----------------------------------------------------------------------------
+Vector CWeaponRPG::GetLaserPosition( void )
+{
+#ifndef CLIENT_DLL
+ CreateLaserPointer();
+
+ if ( m_hLaserDot != NULL )
+ return m_hLaserDot->GetAbsOrigin();
+
+ //FIXME: The laser dot sprite is not active, this code should not be allowed!
+ assert(0);
+#endif
+ return vec3_origin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: NPC RPG users cheat and directly set the laser pointer's origin
+// Input : &vecTarget -
+//-----------------------------------------------------------------------------
+void CWeaponRPG::UpdateNPCLaserPosition( const Vector &vecTarget )
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::SetNPCLaserPosition( const Vector &vecTarget )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CWeaponRPG::GetNPCLaserPosition( void )
+{
+ return vec3_origin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true if the rocket is being guided, false if it's dumb
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::IsGuiding( void )
+{
+ return m_bGuiding;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Deploy( void )
+{
+ m_bInitialStateUpdate = true;
+
+ return BaseClass::Deploy();
+}
+
+bool CWeaponRPG::CanHolster( void )
+{
+ //Can't have an active missile out
+ if ( m_hMissile != NULL )
+ return false;
+
+ return BaseClass::CanHolster();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ StopGuiding();
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn on the guiding laser
+//-----------------------------------------------------------------------------
+void CWeaponRPG::StartGuiding( void )
+{
+ // Don't start back up if we're overriding this
+ if ( m_bHideGuiding )
+ return;
+
+ m_bGuiding = true;
+
+#ifndef CLIENT_DLL
+ WeaponSound(SPECIAL1);
+
+ CreateLaserPointer();
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turn off the guiding laser
+//-----------------------------------------------------------------------------
+void CWeaponRPG::StopGuiding( void )
+{
+ m_bGuiding = false;
+
+#ifndef CLIENT_DLL
+
+ WeaponSound( SPECIAL2 );
+
+ // Kill the dot completely
+ if ( m_hLaserDot != NULL )
+ {
+ m_hLaserDot->TurnOff();
+ UTIL_Remove( m_hLaserDot );
+ m_hLaserDot = NULL;
+ }
+#else
+ if ( m_pBeam )
+ {
+ //Tell it to die right away and let the beam code free it.
+ m_pBeam->brightness = 0.0f;
+ m_pBeam->flags &= ~FBEAM_FOREVER;
+ m_pBeam->die = gpGlobals->curtime - 0.1;
+ m_pBeam = NULL;
+ }
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Toggle the guiding laser
+//-----------------------------------------------------------------------------
+void CWeaponRPG::ToggleGuiding( void )
+{
+ if ( IsGuiding() )
+ {
+ StopGuiding();
+ }
+ else
+ {
+ StartGuiding();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::Drop( const Vector &vecVelocity )
+{
+ StopGuiding();
+
+ BaseClass::Drop( vecVelocity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::UpdateLaserPosition( Vector vecMuzzlePos, Vector vecEndPos )
+{
+
+#ifndef CLIENT_DLL
+ if ( vecMuzzlePos == vec3_origin || vecEndPos == vec3_origin )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ vecMuzzlePos = pPlayer->Weapon_ShootPosition();
+ Vector forward;
+ pPlayer->EyeVectors( &forward );
+ vecEndPos = vecMuzzlePos + ( forward * MAX_TRACE_LENGTH );
+ }
+
+ //Move the laser dot, if active
+ trace_t tr;
+
+ // Trace out for the endpoint
+ UTIL_TraceLine( vecMuzzlePos, vecEndPos, (MASK_SHOT & ~CONTENTS_WINDOW), GetOwner(), 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;
+
+ if ( ( pHit != NULL ) && ( pHit->m_takedamage ) )
+ {
+ m_hLaserDot->SetTargetEntity( pHit );
+ }
+ else
+ {
+ m_hLaserDot->SetTargetEntity( NULL );
+ }
+ }
+ else
+ {
+ m_hLaserDot->SetTargetEntity( NULL );
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::CreateLaserPointer( void )
+{
+#ifndef CLIENT_DLL
+ if ( m_hLaserDot != NULL )
+ return;
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ return;
+
+ m_hLaserDot = CLaserDot::Create( GetAbsOrigin(), GetOwner() );
+ m_hLaserDot->TurnOff();
+
+ UpdateLaserPosition();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRPG::NotifyRocketDied( void )
+{
+ m_hMissile = NULL;
+
+ if ( GetActivity() == ACT_VM_RELOAD )
+ return;
+
+ Reload();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::Reload( void )
+{
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return false;
+
+ if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ return false;
+
+ WeaponSound( RELOAD );
+
+ SendWeaponAnim( ACT_VM_RELOAD );
+
+ return true;
+}
+
+#ifdef CLIENT_DLL
+
+#define RPG_MUZZLE_ATTACHMENT 1
+#define RPG_GUIDE_ATTACHMENT 2
+#define RPG_GUIDE_TARGET_ATTACHMENT 3
+
+#define RPG_GUIDE_ATTACHMENT_3RD 4
+#define RPG_GUIDE_TARGET_ATTACHMENT_3RD 5
+
+#define RPG_LASER_BEAM_LENGTH 128
+
+extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the attachment point on either the world or viewmodel
+// This should really be worked into the CBaseCombatWeapon class!
+//-----------------------------------------------------------------------------
+void CWeaponRPG::GetWeaponAttachment( int attachmentId, Vector &outVector, Vector *dir /*= NULL*/ )
+{
+ QAngle angles;
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner != NULL )
+ {
+ pOwner->GetViewModel()->GetAttachment( attachmentId, outVector, angles );
+ ::FormatViewModelAttachment( outVector, true );
+ }
+ }
+ else
+ {
+ // We offset the IDs to make them correct for our world model
+ BaseClass::GetAttachment( attachmentId, outVector, angles );
+ }
+
+ // Supply the direction, if requested
+ if ( dir != NULL )
+ {
+ AngleVectors( angles, dir, NULL, NULL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup our laser beam
+//-----------------------------------------------------------------------------
+void CWeaponRPG::InitBeam( void )
+{
+ if ( m_pBeam != NULL )
+ return;
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ if ( pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ return;
+
+
+ BeamInfo_t beamInfo;
+
+ CBaseEntity *pEntity = NULL;
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+
+ if ( pOwner != NULL )
+ {
+ pEntity = pOwner->GetViewModel();
+ }
+ }
+ else
+ {
+ pEntity = this;
+ }
+
+ beamInfo.m_pStartEnt = pEntity;
+ beamInfo.m_pEndEnt = pEntity;
+ beamInfo.m_nType = TE_BEAMPOINTS;
+ beamInfo.m_vecStart = vec3_origin;
+ beamInfo.m_vecEnd = vec3_origin;
+
+ beamInfo.m_pszModelName = ( ShouldDrawUsingViewModel() ) ? RPG_BEAM_SPRITE_NOZ : RPG_BEAM_SPRITE;
+
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ beamInfo.m_flWidth = 2.0f;
+ beamInfo.m_flEndWidth = 2.0f;
+ beamInfo.m_nStartAttachment = RPG_GUIDE_ATTACHMENT;
+ beamInfo.m_nEndAttachment = RPG_GUIDE_TARGET_ATTACHMENT;
+ }
+ else
+ {
+ beamInfo.m_flWidth = 1.0f;
+ beamInfo.m_flEndWidth = 1.0f;
+ beamInfo.m_nStartAttachment = RPG_GUIDE_ATTACHMENT_3RD;
+ beamInfo.m_nEndAttachment = RPG_GUIDE_TARGET_ATTACHMENT_3RD;
+ }
+
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 0;
+ beamInfo.m_flBrightness = 255.0;
+ beamInfo.m_flSpeed = 1.0f;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 30.0;
+ beamInfo.m_flRed = 255.0;
+ beamInfo.m_flGreen = 0.0;
+ beamInfo.m_flBlue = 0.0;
+ beamInfo.m_nSegments = 4;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = (FBEAM_FOREVER|FBEAM_SHADEOUT);
+
+ m_pBeam = beams->CreateBeamEntPoint( beamInfo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw effects for our weapon
+//-----------------------------------------------------------------------------
+void CWeaponRPG::DrawEffects( void )
+{
+ // Must be guiding and not hidden
+ if ( !m_bGuiding || m_bHideGuiding )
+ {
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->brightness = 0;
+ }
+
+ return;
+ }
+
+ // Setup our sprite
+ if ( m_hSpriteMaterial == NULL )
+ {
+ m_hSpriteMaterial.Init( RPG_LASER_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+
+ // Setup our beam
+ if ( m_hBeamMaterial == NULL )
+ {
+ m_hBeamMaterial.Init( RPG_BEAM_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+
+ color32 color={255,255,255,255};
+ Vector vecAttachment, vecDir;
+ QAngle angles;
+
+ float scale = 8.0f + random->RandomFloat( -2.0f, 2.0f );
+
+ int attachmentID = ( ShouldDrawUsingViewModel() ) ? RPG_GUIDE_ATTACHMENT : RPG_GUIDE_ATTACHMENT_3RD;
+
+ GetWeaponAttachment( attachmentID, vecAttachment, &vecDir );
+
+ // Draw the sprite
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( m_hSpriteMaterial, this );
+ DrawSprite( vecAttachment, scale, scale, color );
+
+ // Get the beam's run
+ trace_t tr;
+ UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * RPG_LASER_BEAM_LENGTH ), MASK_SHOT, GetOwner(), COLLISION_GROUP_NONE, &tr );
+
+ InitBeam();
+
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->fadeLength = RPG_LASER_BEAM_LENGTH * tr.fraction;
+ m_pBeam->brightness = random->RandomInt( 128, 200 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called on third-person weapon drawing
+//-----------------------------------------------------------------------------
+int CWeaponRPG::DrawModel( int flags )
+{
+ // Only render these on the transparent pass
+ if ( flags & STUDIO_TRANSPARENCY )
+ {
+ DrawEffects();
+ return 1;
+ }
+
+ // Draw the model as normal
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called after first-person viewmodel is drawn
+//-----------------------------------------------------------------------------
+void CWeaponRPG::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
+{
+ // Draw our laser effects
+ DrawEffects();
+
+ BaseClass::ViewModelDrawn( pBaseViewModel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Used to determine sorting of model when drawn
+//-----------------------------------------------------------------------------
+bool CWeaponRPG::IsTranslucent( void )
+{
+ // Must be guiding and not hidden
+ if ( m_bGuiding && !m_bHideGuiding )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Turns off effects when leaving the PVS
+//-----------------------------------------------------------------------------
+void CWeaponRPG::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ BaseClass::NotifyShouldTransmit(state);
+
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ if ( m_pBeam != NULL )
+ {
+ m_pBeam->brightness = 0.0f;
+ }
+ }
+}
+
+#endif //CLIENT_DLL
+
+
+//=============================================================================
+// Laser Dot
+//=============================================================================
+
+LINK_ENTITY_TO_CLASS( env_laserdot, CLaserDot );
+
+BEGIN_DATADESC( CLaserDot )
+ DEFINE_FIELD( m_vecSurfaceNormal, FIELD_VECTOR ),
+ DEFINE_FIELD( m_hTargetEnt, FIELD_EHANDLE ),
+ DEFINE_FIELD( m_bVisibleLaserDot, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bIsOn, FIELD_BOOLEAN ),
+
+ //DEFINE_FIELD( m_pNext, FIELD_CLASSPTR ), // don't save - regenerated by constructor
+END_DATADESC()
+
+
+//-----------------------------------------------------------------------------
+// Finds missiles in cone
+//-----------------------------------------------------------------------------
+CBaseEntity *CreateLaserDot( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
+{
+ return CLaserDot::Create( origin, pOwner, bVisibleDot );
+}
+
+void SetLaserDotTarget( CBaseEntity *pLaserDot, CBaseEntity *pTarget )
+{
+ CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot );
+ pDot->SetTargetEntity( pTarget );
+}
+
+void EnableLaserDot( CBaseEntity *pLaserDot, bool bEnable )
+{
+ CLaserDot *pDot = assert_cast< CLaserDot* >(pLaserDot );
+ if ( bEnable )
+ {
+ pDot->TurnOn();
+ }
+ else
+ {
+ pDot->TurnOff();
+ }
+}
+
+CLaserDot::CLaserDot( void )
+{
+ m_hTargetEnt = NULL;
+ m_bIsOn = true;
+#ifndef CLIENT_DLL
+ g_LaserDotList.Insert( this );
+#endif
+}
+
+CLaserDot::~CLaserDot( void )
+{
+#ifndef CLIENT_DLL
+ g_LaserDotList.Remove( this );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &origin -
+// Output : CLaserDot
+//-----------------------------------------------------------------------------
+CLaserDot *CLaserDot::Create( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot )
+{
+#ifndef CLIENT_DLL
+ CLaserDot *pLaserDot = (CLaserDot *) CBaseEntity::Create( "env_laserdot", origin, QAngle(0,0,0) );
+
+ if ( pLaserDot == NULL )
+ return NULL;
+
+ pLaserDot->m_bVisibleLaserDot = bVisibleDot;
+ pLaserDot->SetMoveType( MOVETYPE_NONE );
+ pLaserDot->AddSolidFlags( FSOLID_NOT_SOLID );
+ pLaserDot->AddEffects( EF_NOSHADOW );
+ UTIL_SetSize( pLaserDot, -Vector(4,4,4), Vector(4,4,4) );
+
+ pLaserDot->SetOwnerEntity( pOwner );
+
+ pLaserDot->AddEFlags( EFL_FORCE_CHECK_TRANSMIT );
+
+ if ( !bVisibleDot )
+ {
+ pLaserDot->MakeInvisible();
+ }
+
+ return pLaserDot;
+#else
+ return NULL;
+#endif
+}
+
+void CLaserDot::SetLaserPosition( const Vector &origin, const Vector &normal )
+{
+ SetAbsOrigin( origin );
+ m_vecSurfaceNormal = normal;
+}
+
+Vector CLaserDot::GetChasePosition()
+{
+ return GetAbsOrigin() - m_vecSurfaceNormal * 10;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLaserDot::TurnOn( void )
+{
+ m_bIsOn = true;
+ if ( m_bVisibleLaserDot )
+ {
+ //BaseClass::TurnOn();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLaserDot::TurnOff( void )
+{
+ m_bIsOn = false;
+ if ( m_bVisibleLaserDot )
+ {
+ //BaseClass::TurnOff();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLaserDot::MakeInvisible( void )
+{
+}
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw our sprite
+//-----------------------------------------------------------------------------
+int CLaserDot::DrawModel( int flags )
+{
+ color32 color={255,255,255,255};
+ Vector vecAttachment, vecDir;
+ QAngle angles;
+
+ float scale;
+ Vector endPos;
+
+ C_HL2MP_Player *pOwner = ToHL2MPPlayer( GetOwnerEntity() );
+
+ if ( pOwner != NULL && pOwner->IsDormant() == false )
+ {
+ // Always draw the dot in front of our faces when in first-person
+ if ( pOwner->IsLocalPlayer() )
+ {
+ // Take our view position and orientation
+ vecAttachment = CurrentViewOrigin();
+ vecDir = CurrentViewForward();
+ }
+ else
+ {
+ // Take the eye position and direction
+ vecAttachment = pOwner->EyePosition();
+
+ QAngle angles = pOwner->GetAnimEyeAngles();
+ AngleVectors( angles, &vecDir );
+ }
+
+ trace_t tr;
+ UTIL_TraceLine( vecAttachment, vecAttachment + ( vecDir * MAX_TRACE_LENGTH ), MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ // Backup off the hit plane
+ endPos = tr.endpos + ( tr.plane.normal * 4.0f );
+ }
+ else
+ {
+ // Just use our position if we can't predict it otherwise
+ endPos = GetAbsOrigin();
+ }
+
+ // Randomly flutter
+ scale = 16.0f + random->RandomFloat( -4.0f, 4.0f );
+
+ // Draw our laser dot in space
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->Bind( m_hSpriteMaterial, this );
+ DrawSprite( endPos, scale, scale, color );
+
+ return 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup our sprite reference
+//-----------------------------------------------------------------------------
+void CLaserDot::OnDataChanged( DataUpdateType_t updateType )
+{
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ m_hSpriteMaterial.Init( RPG_LASER_SPRITE, TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+}
+
+#endif //CLIENT_DLL
diff --git a/mp/src/game/shared/hl2mp/weapon_rpg.h b/mp/src/game/shared/hl2mp/weapon_rpg.h
index 987e7d2f..bbd6f2dd 100644
--- a/mp/src/game/shared/hl2mp/weapon_rpg.h
+++ b/mp/src/game/shared/hl2mp/weapon_rpg.h
@@ -1,267 +1,267 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef WEAPON_RPG_H
-#define WEAPON_RPG_H
-
-#ifdef _WIN32
-#pragma once
-#endif
-
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-
-#ifdef CLIENT_DLL
-
- #include "iviewrender_beams.h"
-
-#endif
-
-#ifndef CLIENT_DLL
-#include "Sprite.h"
-#include "npcevent.h"
-#include "beam_shared.h"
-
-class CWeaponRPG;
-class CLaserDot;
-class RocketTrail;
-
-//###########################################################################
-// >> CMissile (missile launcher class is below this one!)
-//###########################################################################
-class CMissile : public CBaseCombatCharacter
-{
- DECLARE_CLASS( CMissile, CBaseCombatCharacter );
-
-public:
- CMissile();
- ~CMissile();
-
-#ifdef HL1_DLL
- Class_T Classify( void ) { return CLASS_NONE; }
-#else
- Class_T Classify( void ) { return CLASS_MISSILE; }
-#endif
-
- void Spawn( void );
- void Precache( void );
- void MissileTouch( CBaseEntity *pOther );
- void Explode( void );
- void ShotDown( void );
- void AccelerateThink( void );
- void AugerThink( void );
- void IgniteThink( void );
- void SeekThink( void );
- void DumbFire( void );
- void SetGracePeriod( float flGracePeriod );
-
- int OnTakeDamage_Alive( const CTakeDamageInfo &info );
- void Event_Killed( const CTakeDamageInfo &info );
-
- virtual float GetDamage() { return m_flDamage; }
- virtual void SetDamage(float flDamage) { m_flDamage = flDamage; }
-
- unsigned int PhysicsSolidMaskForEntity( void ) const;
-
- CHandle<CWeaponRPG> m_hOwner;
-
- static CMissile *Create( const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner );
-
-protected:
- virtual void DoExplosion();
- virtual void ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed );
- virtual int AugerHealth() { return m_iMaxHealth - 20; }
-
- // Creates the smoke trail
- void CreateSmokeTrail( void );
-
- // Gets the shooting position
- void GetShootPosition( CLaserDot *pLaserDot, Vector *pShootPosition );
-
- CHandle<RocketTrail> m_hRocketTrail;
- float m_flAugerTime; // Amount of time to auger before blowing up anyway
- float m_flMarkDeadTime;
- float m_flDamage;
-
-private:
- float m_flGracePeriodEndsAt;
-
- DECLARE_DATADESC();
-};
-
-
-//-----------------------------------------------------------------------------
-// Laser dot control
-//-----------------------------------------------------------------------------
-CBaseEntity *CreateLaserDot( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot );
-void SetLaserDotTarget( CBaseEntity *pLaserDot, CBaseEntity *pTarget );
-void EnableLaserDot( CBaseEntity *pLaserDot, bool bEnable );
-
-
-//-----------------------------------------------------------------------------
-// Specialized mizzizzile
-//-----------------------------------------------------------------------------
-class CAPCMissile : public CMissile
-{
- DECLARE_CLASS( CMissile, CMissile );
- DECLARE_DATADESC();
-
-public:
- static CAPCMissile *Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, CBaseEntity *pOwner );
-
- CAPCMissile();
- ~CAPCMissile();
- void IgniteDelay( void );
- void AugerDelay( float flDelayTime );
- void ExplodeDelay( float flDelayTime );
- void DisableGuiding();
-#if defined( HL2_DLL )
- virtual Class_T Classify ( void ) { return CLASS_COMBINE; }
-#endif
-
- void AimAtSpecificTarget( CBaseEntity *pTarget );
- void SetGuidanceHint( const char *pHintName );
-
- CAPCMissile *m_pNext;
-
-protected:
- virtual void DoExplosion();
- virtual void ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed );
- virtual int AugerHealth();
-
-private:
- void Init();
- void ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition );
- void BeginSeekThink();
- void AugerStartThink();
- void ExplodeThink();
- void APCMissileTouch( CBaseEntity *pOther );
-
- float m_flReachedTargetTime;
- float m_flIgnitionTime;
- bool m_bGuidingDisabled;
- float m_flLastHomingSpeed;
- EHANDLE m_hSpecificTarget;
- string_t m_strHint;
-};
-
-
-//-----------------------------------------------------------------------------
-// Finds apc missiles in cone
-//-----------------------------------------------------------------------------
-CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDirection, float flAngle );
-
-#endif
-
-//-----------------------------------------------------------------------------
-// RPG
-//-----------------------------------------------------------------------------
-
-#ifdef CLIENT_DLL
-#define CWeaponRPG C_WeaponRPG
-#endif
-
-class CWeaponRPG : public CBaseHL2MPCombatWeapon
-{
- DECLARE_CLASS( CWeaponRPG, CBaseHL2MPCombatWeapon );
-public:
-
- CWeaponRPG();
- ~CWeaponRPG();
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- void Precache( void );
-
- void PrimaryAttack( void );
- virtual float GetFireRate( void ) { return 1; };
- void ItemPostFrame( void );
-
- void Activate( void );
- void DecrementAmmo( CBaseCombatCharacter *pOwner );
-
- bool Deploy( void );
- bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
- bool Reload( void );
- bool WeaponShouldBeLowered( void );
- bool Lower( void );
-
- bool CanHolster( void );
-
- virtual void Drop( const Vector &vecVelocity );
-
- int GetMinBurst() { return 1; }
- int GetMaxBurst() { return 1; }
- float GetMinRestTime() { return 4.0; }
- float GetMaxRestTime() { return 4.0; }
-
- void StartGuiding( void );
- void StopGuiding( void );
- void ToggleGuiding( void );
- bool IsGuiding( void );
-
- void NotifyRocketDied( void );
-
- bool HasAnyAmmo( void );
-
- void SuppressGuiding( bool state = true );
-
- void CreateLaserPointer( void );
- void UpdateLaserPosition( Vector vecMuzzlePos = vec3_origin, Vector vecEndPos = vec3_origin );
- Vector GetLaserPosition( void );
-
- // 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 );
-
-#ifdef CLIENT_DLL
-
- // We need to render opaque and translucent pieces
- virtual RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TWOPASS; }
-
- virtual void NotifyShouldTransmit( ShouldTransmitState_t state );
- virtual int DrawModel( int flags );
- virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
- virtual bool IsTranslucent( void );
-
- void InitBeam( void );
- void GetWeaponAttachment( int attachmentId, Vector &outVector, Vector *dir = NULL );
- void DrawEffects( void );
-// void DrawLaserDot( void );
-
- CMaterialReference m_hSpriteMaterial; // Used for the laser glint
- CMaterialReference m_hBeamMaterial; // Used for the laser beam
- Beam_t *m_pBeam; // Laser beam temp entity
-
-#endif //CLIENT_DLL
-
- CBaseEntity *GetMissile( void ) { return m_hMissile; }
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-
-protected:
-
- CNetworkVar( bool, m_bInitialStateUpdate );
- CNetworkVar( bool, m_bGuiding );
- CNetworkVar( bool, m_bHideGuiding );
-
- CNetworkHandle( CBaseEntity, m_hMissile );
- CNetworkVar( Vector, m_vecLaserDot );
-
-#ifndef CLIENT_DLL
- CHandle<CLaserDot> m_hLaserDot;
-#endif
-
-private:
-
- CWeaponRPG( const CWeaponRPG & );
-};
-
-#endif // WEAPON_RPG_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_RPG_H
+#define WEAPON_RPG_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+
+#ifdef CLIENT_DLL
+
+ #include "iviewrender_beams.h"
+
+#endif
+
+#ifndef CLIENT_DLL
+#include "Sprite.h"
+#include "npcevent.h"
+#include "beam_shared.h"
+
+class CWeaponRPG;
+class CLaserDot;
+class RocketTrail;
+
+//###########################################################################
+// >> CMissile (missile launcher class is below this one!)
+//###########################################################################
+class CMissile : public CBaseCombatCharacter
+{
+ DECLARE_CLASS( CMissile, CBaseCombatCharacter );
+
+public:
+ CMissile();
+ ~CMissile();
+
+#ifdef HL1_DLL
+ Class_T Classify( void ) { return CLASS_NONE; }
+#else
+ Class_T Classify( void ) { return CLASS_MISSILE; }
+#endif
+
+ void Spawn( void );
+ void Precache( void );
+ void MissileTouch( CBaseEntity *pOther );
+ void Explode( void );
+ void ShotDown( void );
+ void AccelerateThink( void );
+ void AugerThink( void );
+ void IgniteThink( void );
+ void SeekThink( void );
+ void DumbFire( void );
+ void SetGracePeriod( float flGracePeriod );
+
+ int OnTakeDamage_Alive( const CTakeDamageInfo &info );
+ void Event_Killed( const CTakeDamageInfo &info );
+
+ virtual float GetDamage() { return m_flDamage; }
+ virtual void SetDamage(float flDamage) { m_flDamage = flDamage; }
+
+ unsigned int PhysicsSolidMaskForEntity( void ) const;
+
+ CHandle<CWeaponRPG> m_hOwner;
+
+ static CMissile *Create( const Vector &vecOrigin, const QAngle &vecAngles, edict_t *pentOwner );
+
+protected:
+ virtual void DoExplosion();
+ virtual void ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed );
+ virtual int AugerHealth() { return m_iMaxHealth - 20; }
+
+ // Creates the smoke trail
+ void CreateSmokeTrail( void );
+
+ // Gets the shooting position
+ void GetShootPosition( CLaserDot *pLaserDot, Vector *pShootPosition );
+
+ CHandle<RocketTrail> m_hRocketTrail;
+ float m_flAugerTime; // Amount of time to auger before blowing up anyway
+ float m_flMarkDeadTime;
+ float m_flDamage;
+
+private:
+ float m_flGracePeriodEndsAt;
+
+ DECLARE_DATADESC();
+};
+
+
+//-----------------------------------------------------------------------------
+// Laser dot control
+//-----------------------------------------------------------------------------
+CBaseEntity *CreateLaserDot( const Vector &origin, CBaseEntity *pOwner, bool bVisibleDot );
+void SetLaserDotTarget( CBaseEntity *pLaserDot, CBaseEntity *pTarget );
+void EnableLaserDot( CBaseEntity *pLaserDot, bool bEnable );
+
+
+//-----------------------------------------------------------------------------
+// Specialized mizzizzile
+//-----------------------------------------------------------------------------
+class CAPCMissile : public CMissile
+{
+ DECLARE_CLASS( CMissile, CMissile );
+ DECLARE_DATADESC();
+
+public:
+ static CAPCMissile *Create( const Vector &vecOrigin, const QAngle &vecAngles, const Vector &vecVelocity, CBaseEntity *pOwner );
+
+ CAPCMissile();
+ ~CAPCMissile();
+ void IgniteDelay( void );
+ void AugerDelay( float flDelayTime );
+ void ExplodeDelay( float flDelayTime );
+ void DisableGuiding();
+#if defined( HL2_DLL )
+ virtual Class_T Classify ( void ) { return CLASS_COMBINE; }
+#endif
+
+ void AimAtSpecificTarget( CBaseEntity *pTarget );
+ void SetGuidanceHint( const char *pHintName );
+
+ CAPCMissile *m_pNext;
+
+protected:
+ virtual void DoExplosion();
+ virtual void ComputeActualDotPosition( CLaserDot *pLaserDot, Vector *pActualDotPosition, float *pHomingSpeed );
+ virtual int AugerHealth();
+
+private:
+ void Init();
+ void ComputeLeadingPosition( const Vector &vecShootPosition, CBaseEntity *pTarget, Vector *pLeadPosition );
+ void BeginSeekThink();
+ void AugerStartThink();
+ void ExplodeThink();
+ void APCMissileTouch( CBaseEntity *pOther );
+
+ float m_flReachedTargetTime;
+ float m_flIgnitionTime;
+ bool m_bGuidingDisabled;
+ float m_flLastHomingSpeed;
+ EHANDLE m_hSpecificTarget;
+ string_t m_strHint;
+};
+
+
+//-----------------------------------------------------------------------------
+// Finds apc missiles in cone
+//-----------------------------------------------------------------------------
+CAPCMissile *FindAPCMissileInCone( const Vector &vecOrigin, const Vector &vecDirection, float flAngle );
+
+#endif
+
+//-----------------------------------------------------------------------------
+// RPG
+//-----------------------------------------------------------------------------
+
+#ifdef CLIENT_DLL
+#define CWeaponRPG C_WeaponRPG
+#endif
+
+class CWeaponRPG : public CBaseHL2MPCombatWeapon
+{
+ DECLARE_CLASS( CWeaponRPG, CBaseHL2MPCombatWeapon );
+public:
+
+ CWeaponRPG();
+ ~CWeaponRPG();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ void Precache( void );
+
+ void PrimaryAttack( void );
+ virtual float GetFireRate( void ) { return 1; };
+ void ItemPostFrame( void );
+
+ void Activate( void );
+ void DecrementAmmo( CBaseCombatCharacter *pOwner );
+
+ bool Deploy( void );
+ bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ bool Reload( void );
+ bool WeaponShouldBeLowered( void );
+ bool Lower( void );
+
+ bool CanHolster( void );
+
+ virtual void Drop( const Vector &vecVelocity );
+
+ int GetMinBurst() { return 1; }
+ int GetMaxBurst() { return 1; }
+ float GetMinRestTime() { return 4.0; }
+ float GetMaxRestTime() { return 4.0; }
+
+ void StartGuiding( void );
+ void StopGuiding( void );
+ void ToggleGuiding( void );
+ bool IsGuiding( void );
+
+ void NotifyRocketDied( void );
+
+ bool HasAnyAmmo( void );
+
+ void SuppressGuiding( bool state = true );
+
+ void CreateLaserPointer( void );
+ void UpdateLaserPosition( Vector vecMuzzlePos = vec3_origin, Vector vecEndPos = vec3_origin );
+ Vector GetLaserPosition( void );
+
+ // 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 );
+
+#ifdef CLIENT_DLL
+
+ // We need to render opaque and translucent pieces
+ virtual RenderGroup_t GetRenderGroup( void ) { return RENDER_GROUP_TWOPASS; }
+
+ virtual void NotifyShouldTransmit( ShouldTransmitState_t state );
+ virtual int DrawModel( int flags );
+ virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
+ virtual bool IsTranslucent( void );
+
+ void InitBeam( void );
+ void GetWeaponAttachment( int attachmentId, Vector &outVector, Vector *dir = NULL );
+ void DrawEffects( void );
+// void DrawLaserDot( void );
+
+ CMaterialReference m_hSpriteMaterial; // Used for the laser glint
+ CMaterialReference m_hBeamMaterial; // Used for the laser beam
+ Beam_t *m_pBeam; // Laser beam temp entity
+
+#endif //CLIENT_DLL
+
+ CBaseEntity *GetMissile( void ) { return m_hMissile; }
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+
+protected:
+
+ CNetworkVar( bool, m_bInitialStateUpdate );
+ CNetworkVar( bool, m_bGuiding );
+ CNetworkVar( bool, m_bHideGuiding );
+
+ CNetworkHandle( CBaseEntity, m_hMissile );
+ CNetworkVar( Vector, m_vecLaserDot );
+
+#ifndef CLIENT_DLL
+ CHandle<CLaserDot> m_hLaserDot;
+#endif
+
+private:
+
+ CWeaponRPG( const CWeaponRPG & );
+};
+
+#endif // WEAPON_RPG_H
diff --git a/mp/src/game/shared/hl2mp/weapon_shotgun.cpp b/mp/src/game/shared/hl2mp/weapon_shotgun.cpp
index 504f267a..da370b53 100644
--- a/mp/src/game/shared/hl2mp/weapon_shotgun.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_shotgun.cpp
@@ -1,634 +1,634 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "in_buttons.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
-#else
- #include "hl2mp_player.h"
-#endif
-
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-
-#ifdef CLIENT_DLL
-#define CWeaponShotgun C_WeaponShotgun
-#endif
-
-extern ConVar sk_auto_reload_time;
-extern ConVar sk_plr_num_shotgun_pellets;
-
-class CWeaponShotgun : public CBaseHL2MPCombatWeapon
-{
-public:
- DECLARE_CLASS( CWeaponShotgun, CBaseHL2MPCombatWeapon );
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
-private:
- CNetworkVar( bool, m_bNeedPump ); // When emptied completely
- CNetworkVar( bool, m_bDelayedFire1 ); // Fire primary when finished reloading
- CNetworkVar( bool, m_bDelayedFire2 ); // Fire secondary when finished reloading
- CNetworkVar( bool, m_bDelayedReload ); // Reload when finished pump
-
-public:
- virtual const Vector& GetBulletSpread( void )
- {
- static Vector cone = VECTOR_CONE_10DEGREES;
- return cone;
- }
-
- virtual int GetMinBurst() { return 1; }
- virtual int GetMaxBurst() { return 3; }
-
- bool StartReload( void );
- bool Reload( void );
- void FillClip( void );
- void FinishReload( void );
- void CheckHolsterReload( void );
- void Pump( void );
-// void WeaponIdle( void );
- void ItemHolsterFrame( void );
- void ItemPostFrame( void );
- void PrimaryAttack( void );
- void SecondaryAttack( void );
- void DryFire( void );
- virtual float GetFireRate( void ) { return 0.7; };
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-
- CWeaponShotgun(void);
-
-private:
- CWeaponShotgun( const CWeaponShotgun & );
-};
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponShotgun, DT_WeaponShotgun )
-
-BEGIN_NETWORK_TABLE( CWeaponShotgun, DT_WeaponShotgun )
-#ifdef CLIENT_DLL
- RecvPropBool( RECVINFO( m_bNeedPump ) ),
- RecvPropBool( RECVINFO( m_bDelayedFire1 ) ),
- RecvPropBool( RECVINFO( m_bDelayedFire2 ) ),
- RecvPropBool( RECVINFO( m_bDelayedReload ) ),
-#else
- SendPropBool( SENDINFO( m_bNeedPump ) ),
- SendPropBool( SENDINFO( m_bDelayedFire1 ) ),
- SendPropBool( SENDINFO( m_bDelayedFire2 ) ),
- SendPropBool( SENDINFO( m_bDelayedReload ) ),
-#endif
-END_NETWORK_TABLE()
-
-#ifdef CLIENT_DLL
-BEGIN_PREDICTION_DATA( CWeaponShotgun )
- DEFINE_PRED_FIELD( m_bNeedPump, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bDelayedFire1, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bDelayedFire2, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bDelayedReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
-END_PREDICTION_DATA()
-#endif
-
-LINK_ENTITY_TO_CLASS( weapon_shotgun, CWeaponShotgun );
-PRECACHE_WEAPON_REGISTER(weapon_shotgun);
-
-#ifndef CLIENT_DLL
-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 },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponShotgun);
-
-#endif
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Override so only reload one shell at a time
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-bool CWeaponShotgun::StartReload( void )
-{
- if ( m_bNeedPump )
- return false;
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return false;
-
- if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- return false;
-
- if (m_iClip1 >= GetMaxClip1())
- return false;
-
-
- int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
-
- if (j <= 0)
- return false;
-
- SendWeaponAnim( ACT_SHOTGUN_RELOAD_START );
-
- // Make shotgun shell visible
- SetBodygroup(1,0);
-
- pOwner->m_flNextAttack = gpGlobals->curtime;
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-
- m_bInReload = true;
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override so only reload one shell at a time
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-bool CWeaponShotgun::Reload( void )
-{
- // Check that StartReload was called first
- if (!m_bInReload)
- {
- Warning("ERROR: Shotgun Reload called incorrectly!\n");
- }
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return false;
-
- if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- return false;
-
- if (m_iClip1 >= GetMaxClip1())
- return false;
-
- int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
-
- if (j <= 0)
- return false;
-
- FillClip();
- // Play reload on different channel as otherwise steals channel away from fire sound
- WeaponSound(RELOAD);
- SendWeaponAnim( ACT_VM_RELOAD );
-
- pOwner->m_flNextAttack = gpGlobals->curtime;
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play finish reload anim and fill clip
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::FinishReload( void )
-{
- // Make shotgun shell invisible
- SetBodygroup(1,1);
-
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- m_bInReload = false;
-
- // Finish reload animation
- SendWeaponAnim( ACT_SHOTGUN_RELOAD_FINISH );
-
- pOwner->m_flNextAttack = gpGlobals->curtime;
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play finish reload anim and fill clip
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::FillClip( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- // Add them to the clip
- if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
- {
- if ( Clip1() < GetMaxClip1() )
- {
- m_iClip1++;
- pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Play weapon pump anim
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::Pump( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( pOwner == NULL )
- return;
-
- m_bNeedPump = false;
-
- if ( m_bDelayedReload )
- {
- m_bDelayedReload = false;
- StartReload();
- }
-
- WeaponSound( SPECIAL1 );
-
- // Finish reload animation
- SendWeaponAnim( ACT_SHOTGUN_PUMP );
-
- pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration();
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::DryFire( void )
-{
- WeaponSound(EMPTY);
- SendWeaponAnim( ACT_VM_DRYFIRE );
-
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::PrimaryAttack( void )
-{
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if (!pPlayer)
- {
- return;
- }
-
- // MUST call sound before removing a round from the clip of a CMachineGun
- WeaponSound(SINGLE);
-
- pPlayer->DoMuzzleFlash();
-
- SendWeaponAnim( ACT_VM_PRIMARYATTACK );
-
- // Don't fire again until fire animation has completed
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- m_iClip1 -= 1;
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- Vector vecSrc = pPlayer->Weapon_ShootPosition( );
- Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
-
- 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 );
-
- QAngle punch;
- punch.Init( SharedRandomFloat( "shotgunpax", -2, -1 ), SharedRandomFloat( "shotgunpay", -2, 2 ), 0 );
- pPlayer->ViewPunch( punch );
-
- if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- {
- // HEV suit - indicate out of ammo condition
- pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
- }
-
- m_bNeedPump = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//
-//
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::SecondaryAttack( void )
-{
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if (!pPlayer)
- {
- return;
- }
-
- pPlayer->m_nButtons &= ~IN_ATTACK2;
- // MUST call sound before removing a round from the clip of a CMachineGun
- WeaponSound(WPN_DOUBLE);
-
- pPlayer->DoMuzzleFlash();
-
- SendWeaponAnim( ACT_VM_SECONDARYATTACK );
-
- // Don't fire again until fire animation has completed
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- m_iClip1 -= 2; // Shotgun uses same clip for primary and secondary attacks
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- Vector vecSrc = pPlayer->Weapon_ShootPosition();
- Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
-
- FireBulletsInfo_t info( 12, 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 );
- pPlayer->ViewPunch( QAngle(SharedRandomFloat( "shotgunsax", -5, 5 ),0,0) );
-
- if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
- {
- // HEV suit - indicate out of ammo condition
- pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
- }
-
- m_bNeedPump = true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override so shotgun can do mulitple reloads in a row
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::ItemPostFrame( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- if (!pOwner)
- {
- return;
- }
-
- if ( m_bNeedPump && ( pOwner->m_nButtons & IN_RELOAD ) )
- {
- m_bDelayedReload = true;
- }
-
- if (m_bInReload)
- {
- // If I'm primary firing and have one round stop reloading and fire
- if ((pOwner->m_nButtons & IN_ATTACK ) && (m_iClip1 >=1) && !m_bNeedPump )
- {
- m_bInReload = false;
- m_bNeedPump = false;
- m_bDelayedFire1 = true;
- }
- // If I'm secondary firing and have two rounds stop reloading and fire
- else if ((pOwner->m_nButtons & IN_ATTACK2 ) && (m_iClip1 >=2) && !m_bNeedPump )
- {
- m_bInReload = false;
- m_bNeedPump = false;
- m_bDelayedFire2 = true;
- }
- else if (m_flNextPrimaryAttack <= gpGlobals->curtime)
- {
- // If out of ammo end reload
- if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <=0)
- {
- FinishReload();
- return;
- }
- // If clip not full reload again
- if (m_iClip1 < GetMaxClip1())
- {
- Reload();
- return;
- }
- // Clip full, stop reloading
- else
- {
- FinishReload();
- return;
- }
- }
- }
- else
- {
- // Make shotgun shell invisible
- SetBodygroup(1,1);
- }
-
- if ((m_bNeedPump) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
- {
- Pump();
- return;
- }
-
- // Shotgun uses same timing and ammo for secondary attack
- if ((m_bDelayedFire2 || pOwner->m_nButtons & IN_ATTACK2)&&(m_flNextPrimaryAttack <= gpGlobals->curtime))
- {
- m_bDelayedFire2 = false;
-
- if ( (m_iClip1 <= 1 && UsesClipsForAmmo1()))
- {
- // If only one shell is left, do a single shot instead
- if ( m_iClip1 == 1 )
- {
- PrimaryAttack();
- }
- else if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType))
- {
- DryFire();
- }
- else
- {
- StartReload();
- }
- }
-
- // Fire underwater?
- else if (GetOwner()->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
- {
- WeaponSound(EMPTY);
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
- return;
- }
- else
- {
- // If the firing button was just pressed, reset the firing time
- if ( pOwner->m_afButtonPressed & IN_ATTACK )
- {
- m_flNextPrimaryAttack = gpGlobals->curtime;
- }
- SecondaryAttack();
- }
- }
- else if ( (m_bDelayedFire1 || pOwner->m_nButtons & IN_ATTACK) && m_flNextPrimaryAttack <= gpGlobals->curtime)
- {
- m_bDelayedFire1 = false;
- if ( (m_iClip1 <= 0 && UsesClipsForAmmo1()) || ( !UsesClipsForAmmo1() && !pOwner->GetAmmoCount(m_iPrimaryAmmoType) ) )
- {
- if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType))
- {
- DryFire();
- }
- else
- {
- StartReload();
- }
- }
- // Fire underwater?
- else if (pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
- {
- WeaponSound(EMPTY);
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
- return;
- }
- else
- {
- // If the firing button was just pressed, reset the firing time
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
- if ( pPlayer && pPlayer->m_afButtonPressed & IN_ATTACK )
- {
- m_flNextPrimaryAttack = gpGlobals->curtime;
- }
- PrimaryAttack();
- }
- }
-
- if ( pOwner->m_nButtons & IN_RELOAD && UsesClipsForAmmo1() && !m_bInReload )
- {
- // reload when reload is pressed, or if no buttons are down and weapon is empty.
- StartReload();
- }
- else
- {
- // no fire buttons down
- m_bFireOnEmpty = false;
-
- if ( !HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime )
- {
- // weapon isn't useable, switch.
- if ( !(GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && pOwner->SwitchToNextBestWeapon( this ) )
- {
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
- return;
- }
- }
- else
- {
- // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
- if ( m_iClip1 <= 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
- {
- if (StartReload())
- {
- // if we've successfully started to reload, we're done
- return;
- }
- }
- }
-
- WeaponIdle( );
- return;
- }
-
-}
-
-
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-//-----------------------------------------------------------------------------
-CWeaponShotgun::CWeaponShotgun( void )
-{
- m_bReloadsSingly = true;
-
- m_bNeedPump = false;
- m_bDelayedFire1 = false;
- m_bDelayedFire2 = false;
-
- m_fMinRange1 = 0.0;
- m_fMaxRange1 = 500;
- m_fMinRange2 = 0.0;
- m_fMaxRange2 = 200;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponShotgun::ItemHolsterFrame( void )
-{
- // Must be player held
- if ( GetOwner() && GetOwner()->IsPlayer() == false )
- return;
-
- // We can't be active
- if ( GetOwner()->GetActiveWeapon() == this )
- return;
-
- // If it's been longer than three seconds, reload
- if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() )
- {
- // Reset the timer
- m_flHolsterTime = gpGlobals->curtime;
-
- if ( GetOwner() == NULL )
- return;
-
- if ( m_iClip1 == GetMaxClip1() )
- return;
-
- // Just load the clip with no animations
- int ammoFill = MIN( (GetMaxClip1() - m_iClip1), GetOwner()->GetAmmoCount( GetPrimaryAmmoType() ) );
-
- GetOwner()->RemoveAmmo( ammoFill, GetPrimaryAmmoType() );
- m_iClip1 += ammoFill;
- }
-}
-
-//==================================================
-// Purpose:
-//==================================================
-/*
-void CWeaponShotgun::WeaponIdle( void )
-{
- //Only the player fires this way so we can cast
- CBasePlayer *pPlayer = GetOwner()
-
- if ( pPlayer == NULL )
- return;
-
- //If we're on a target, play the new anim
- if ( pPlayer->IsOnTarget() )
- {
- SendWeaponAnim( ACT_VM_IDLE_ACTIVE );
- }
-}
-*/
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+#else
+ #include "hl2mp_player.h"
+#endif
+
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+
+#ifdef CLIENT_DLL
+#define CWeaponShotgun C_WeaponShotgun
+#endif
+
+extern ConVar sk_auto_reload_time;
+extern ConVar sk_plr_num_shotgun_pellets;
+
+class CWeaponShotgun : public CBaseHL2MPCombatWeapon
+{
+public:
+ DECLARE_CLASS( CWeaponShotgun, CBaseHL2MPCombatWeapon );
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+private:
+ CNetworkVar( bool, m_bNeedPump ); // When emptied completely
+ CNetworkVar( bool, m_bDelayedFire1 ); // Fire primary when finished reloading
+ CNetworkVar( bool, m_bDelayedFire2 ); // Fire secondary when finished reloading
+ CNetworkVar( bool, m_bDelayedReload ); // Reload when finished pump
+
+public:
+ virtual const Vector& GetBulletSpread( void )
+ {
+ static Vector cone = VECTOR_CONE_10DEGREES;
+ return cone;
+ }
+
+ virtual int GetMinBurst() { return 1; }
+ virtual int GetMaxBurst() { return 3; }
+
+ bool StartReload( void );
+ bool Reload( void );
+ void FillClip( void );
+ void FinishReload( void );
+ void CheckHolsterReload( void );
+ void Pump( void );
+// void WeaponIdle( void );
+ void ItemHolsterFrame( void );
+ void ItemPostFrame( void );
+ void PrimaryAttack( void );
+ void SecondaryAttack( void );
+ void DryFire( void );
+ virtual float GetFireRate( void ) { return 0.7; };
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+
+ CWeaponShotgun(void);
+
+private:
+ CWeaponShotgun( const CWeaponShotgun & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponShotgun, DT_WeaponShotgun )
+
+BEGIN_NETWORK_TABLE( CWeaponShotgun, DT_WeaponShotgun )
+#ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bNeedPump ) ),
+ RecvPropBool( RECVINFO( m_bDelayedFire1 ) ),
+ RecvPropBool( RECVINFO( m_bDelayedFire2 ) ),
+ RecvPropBool( RECVINFO( m_bDelayedReload ) ),
+#else
+ SendPropBool( SENDINFO( m_bNeedPump ) ),
+ SendPropBool( SENDINFO( m_bDelayedFire1 ) ),
+ SendPropBool( SENDINFO( m_bDelayedFire2 ) ),
+ SendPropBool( SENDINFO( m_bDelayedReload ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponShotgun )
+ DEFINE_PRED_FIELD( m_bNeedPump, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bDelayedFire1, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bDelayedFire2, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bDelayedReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_shotgun, CWeaponShotgun );
+PRECACHE_WEAPON_REGISTER(weapon_shotgun);
+
+#ifndef CLIENT_DLL
+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 },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponShotgun);
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Override so only reload one shell at a time
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool CWeaponShotgun::StartReload( void )
+{
+ if ( m_bNeedPump )
+ return false;
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return false;
+
+ if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
+ return false;
+
+ if (m_iClip1 >= GetMaxClip1())
+ return false;
+
+
+ int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
+
+ if (j <= 0)
+ return false;
+
+ SendWeaponAnim( ACT_SHOTGUN_RELOAD_START );
+
+ // Make shotgun shell visible
+ SetBodygroup(1,0);
+
+ pOwner->m_flNextAttack = gpGlobals->curtime;
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+
+ m_bInReload = true;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override so only reload one shell at a time
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool CWeaponShotgun::Reload( void )
+{
+ // Check that StartReload was called first
+ if (!m_bInReload)
+ {
+ Warning("ERROR: Shotgun Reload called incorrectly!\n");
+ }
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return false;
+
+ if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
+ return false;
+
+ if (m_iClip1 >= GetMaxClip1())
+ return false;
+
+ int j = MIN(1, pOwner->GetAmmoCount(m_iPrimaryAmmoType));
+
+ if (j <= 0)
+ return false;
+
+ FillClip();
+ // Play reload on different channel as otherwise steals channel away from fire sound
+ WeaponSound(RELOAD);
+ SendWeaponAnim( ACT_VM_RELOAD );
+
+ pOwner->m_flNextAttack = gpGlobals->curtime;
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play finish reload anim and fill clip
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeaponShotgun::FinishReload( void )
+{
+ // Make shotgun shell invisible
+ SetBodygroup(1,1);
+
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ m_bInReload = false;
+
+ // Finish reload animation
+ SendWeaponAnim( ACT_SHOTGUN_RELOAD_FINISH );
+
+ pOwner->m_flNextAttack = gpGlobals->curtime;
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play finish reload anim and fill clip
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeaponShotgun::FillClip( void )
+{
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ // Add them to the clip
+ if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
+ {
+ if ( Clip1() < GetMaxClip1() )
+ {
+ m_iClip1++;
+ pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play weapon pump anim
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeaponShotgun::Pump( void )
+{
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ m_bNeedPump = false;
+
+ if ( m_bDelayedReload )
+ {
+ m_bDelayedReload = false;
+ StartReload();
+ }
+
+ WeaponSound( SPECIAL1 );
+
+ // Finish reload animation
+ SendWeaponAnim( ACT_SHOTGUN_PUMP );
+
+ pOwner->m_flNextAttack = gpGlobals->curtime + SequenceDuration();
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+//
+//-----------------------------------------------------------------------------
+void CWeaponShotgun::DryFire( void )
+{
+ WeaponSound(EMPTY);
+ SendWeaponAnim( ACT_VM_DRYFIRE );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+//
+//-----------------------------------------------------------------------------
+void CWeaponShotgun::PrimaryAttack( void )
+{
+ // Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if (!pPlayer)
+ {
+ return;
+ }
+
+ // MUST call sound before removing a round from the clip of a CMachineGun
+ WeaponSound(SINGLE);
+
+ pPlayer->DoMuzzleFlash();
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ // Don't fire again until fire animation has completed
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ m_iClip1 -= 1;
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
+
+ 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 );
+
+ QAngle punch;
+ punch.Init( SharedRandomFloat( "shotgunpax", -2, -1 ), SharedRandomFloat( "shotgunpay", -2, 2 ), 0 );
+ pPlayer->ViewPunch( punch );
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
+ }
+
+ m_bNeedPump = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+//
+//-----------------------------------------------------------------------------
+void CWeaponShotgun::SecondaryAttack( void )
+{
+ // Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if (!pPlayer)
+ {
+ return;
+ }
+
+ pPlayer->m_nButtons &= ~IN_ATTACK2;
+ // MUST call sound before removing a round from the clip of a CMachineGun
+ WeaponSound(WPN_DOUBLE);
+
+ pPlayer->DoMuzzleFlash();
+
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+ // Don't fire again until fire animation has completed
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ m_iClip1 -= 2; // Shotgun uses same clip for primary and secondary attacks
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ Vector vecSrc = pPlayer->Weapon_ShootPosition();
+ Vector vecAiming = pPlayer->GetAutoaimVector( AUTOAIM_10DEGREES );
+
+ FireBulletsInfo_t info( 12, 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 );
+ pPlayer->ViewPunch( QAngle(SharedRandomFloat( "shotgunsax", -5, 5 ),0,0) );
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", FALSE, 0);
+ }
+
+ m_bNeedPump = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override so shotgun can do mulitple reloads in a row
+//-----------------------------------------------------------------------------
+void CWeaponShotgun::ItemPostFrame( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if (!pOwner)
+ {
+ return;
+ }
+
+ if ( m_bNeedPump && ( pOwner->m_nButtons & IN_RELOAD ) )
+ {
+ m_bDelayedReload = true;
+ }
+
+ if (m_bInReload)
+ {
+ // If I'm primary firing and have one round stop reloading and fire
+ if ((pOwner->m_nButtons & IN_ATTACK ) && (m_iClip1 >=1) && !m_bNeedPump )
+ {
+ m_bInReload = false;
+ m_bNeedPump = false;
+ m_bDelayedFire1 = true;
+ }
+ // If I'm secondary firing and have two rounds stop reloading and fire
+ else if ((pOwner->m_nButtons & IN_ATTACK2 ) && (m_iClip1 >=2) && !m_bNeedPump )
+ {
+ m_bInReload = false;
+ m_bNeedPump = false;
+ m_bDelayedFire2 = true;
+ }
+ else if (m_flNextPrimaryAttack <= gpGlobals->curtime)
+ {
+ // If out of ammo end reload
+ if (pOwner->GetAmmoCount(m_iPrimaryAmmoType) <=0)
+ {
+ FinishReload();
+ return;
+ }
+ // If clip not full reload again
+ if (m_iClip1 < GetMaxClip1())
+ {
+ Reload();
+ return;
+ }
+ // Clip full, stop reloading
+ else
+ {
+ FinishReload();
+ return;
+ }
+ }
+ }
+ else
+ {
+ // Make shotgun shell invisible
+ SetBodygroup(1,1);
+ }
+
+ if ((m_bNeedPump) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
+ {
+ Pump();
+ return;
+ }
+
+ // Shotgun uses same timing and ammo for secondary attack
+ if ((m_bDelayedFire2 || pOwner->m_nButtons & IN_ATTACK2)&&(m_flNextPrimaryAttack <= gpGlobals->curtime))
+ {
+ m_bDelayedFire2 = false;
+
+ if ( (m_iClip1 <= 1 && UsesClipsForAmmo1()))
+ {
+ // If only one shell is left, do a single shot instead
+ if ( m_iClip1 == 1 )
+ {
+ PrimaryAttack();
+ }
+ else if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType))
+ {
+ DryFire();
+ }
+ else
+ {
+ StartReload();
+ }
+ }
+
+ // Fire underwater?
+ else if (GetOwner()->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
+ {
+ WeaponSound(EMPTY);
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
+ return;
+ }
+ else
+ {
+ // If the firing button was just pressed, reset the firing time
+ if ( pOwner->m_afButtonPressed & IN_ATTACK )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime;
+ }
+ SecondaryAttack();
+ }
+ }
+ else if ( (m_bDelayedFire1 || pOwner->m_nButtons & IN_ATTACK) && m_flNextPrimaryAttack <= gpGlobals->curtime)
+ {
+ m_bDelayedFire1 = false;
+ if ( (m_iClip1 <= 0 && UsesClipsForAmmo1()) || ( !UsesClipsForAmmo1() && !pOwner->GetAmmoCount(m_iPrimaryAmmoType) ) )
+ {
+ if (!pOwner->GetAmmoCount(m_iPrimaryAmmoType))
+ {
+ DryFire();
+ }
+ else
+ {
+ StartReload();
+ }
+ }
+ // Fire underwater?
+ else if (pOwner->GetWaterLevel() == 3 && m_bFiresUnderwater == false)
+ {
+ WeaponSound(EMPTY);
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
+ return;
+ }
+ else
+ {
+ // If the firing button was just pressed, reset the firing time
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( pPlayer && pPlayer->m_afButtonPressed & IN_ATTACK )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime;
+ }
+ PrimaryAttack();
+ }
+ }
+
+ if ( pOwner->m_nButtons & IN_RELOAD && UsesClipsForAmmo1() && !m_bInReload )
+ {
+ // reload when reload is pressed, or if no buttons are down and weapon is empty.
+ StartReload();
+ }
+ else
+ {
+ // no fire buttons down
+ m_bFireOnEmpty = false;
+
+ if ( !HasAnyAmmo() && m_flNextPrimaryAttack < gpGlobals->curtime )
+ {
+ // weapon isn't useable, switch.
+ if ( !(GetWeaponFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY) && pOwner->SwitchToNextBestWeapon( this ) )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
+ return;
+ }
+ }
+ else
+ {
+ // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
+ if ( m_iClip1 <= 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
+ {
+ if (StartReload())
+ {
+ // if we've successfully started to reload, we're done
+ return;
+ }
+ }
+ }
+
+ WeaponIdle( );
+ return;
+ }
+
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CWeaponShotgun::CWeaponShotgun( void )
+{
+ m_bReloadsSingly = true;
+
+ m_bNeedPump = false;
+ m_bDelayedFire1 = false;
+ m_bDelayedFire2 = false;
+
+ m_fMinRange1 = 0.0;
+ m_fMaxRange1 = 500;
+ m_fMinRange2 = 0.0;
+ m_fMaxRange2 = 200;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponShotgun::ItemHolsterFrame( void )
+{
+ // Must be player held
+ if ( GetOwner() && GetOwner()->IsPlayer() == false )
+ return;
+
+ // We can't be active
+ if ( GetOwner()->GetActiveWeapon() == this )
+ return;
+
+ // If it's been longer than three seconds, reload
+ if ( ( gpGlobals->curtime - m_flHolsterTime ) > sk_auto_reload_time.GetFloat() )
+ {
+ // Reset the timer
+ m_flHolsterTime = gpGlobals->curtime;
+
+ if ( GetOwner() == NULL )
+ return;
+
+ if ( m_iClip1 == GetMaxClip1() )
+ return;
+
+ // Just load the clip with no animations
+ int ammoFill = MIN( (GetMaxClip1() - m_iClip1), GetOwner()->GetAmmoCount( GetPrimaryAmmoType() ) );
+
+ GetOwner()->RemoveAmmo( ammoFill, GetPrimaryAmmoType() );
+ m_iClip1 += ammoFill;
+ }
+}
+
+//==================================================
+// Purpose:
+//==================================================
+/*
+void CWeaponShotgun::WeaponIdle( void )
+{
+ //Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = GetOwner()
+
+ if ( pPlayer == NULL )
+ return;
+
+ //If we're on a target, play the new anim
+ if ( pPlayer->IsOnTarget() )
+ {
+ SendWeaponAnim( ACT_VM_IDLE_ACTIVE );
+ }
+}
+*/
diff --git a/mp/src/game/shared/hl2mp/weapon_slam.cpp b/mp/src/game/shared/hl2mp/weapon_slam.cpp
index 4e5bff29..bcdec40c 100644
--- a/mp/src/game/shared/hl2mp/weapon_slam.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_slam.cpp
@@ -1,1052 +1,1052 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "gamerules.h"
-#include "npcevent.h"
-#include "in_buttons.h"
-#include "engine/IEngineSound.h"
-
-#if defined( CLIENT_DLL )
- #include "c_hl2mp_player.h"
-#else
- #include "hl2mp_player.h"
- #include "grenade_tripmine.h"
- #include "grenade_satchel.h"
- #include "entitylist.h"
- #include "eventqueue.h"
-#endif
-
-#include "hl2mp/weapon_slam.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define SLAM_PRIMARY_VOLUME 450
-
-IMPLEMENT_NETWORKCLASS_ALIASED( Weapon_SLAM, DT_Weapon_SLAM )
-
-BEGIN_NETWORK_TABLE( CWeapon_SLAM, DT_Weapon_SLAM )
-#ifdef CLIENT_DLL
- RecvPropInt( RECVINFO( m_tSlamState ) ),
- RecvPropBool( RECVINFO( m_bDetonatorArmed ) ),
- RecvPropBool( RECVINFO( m_bNeedDetonatorDraw ) ),
- RecvPropBool( RECVINFO( m_bNeedDetonatorHolster ) ),
- RecvPropBool( RECVINFO( m_bNeedReload ) ),
- RecvPropBool( RECVINFO( m_bClearReload ) ),
- RecvPropBool( RECVINFO( m_bThrowSatchel ) ),
- RecvPropBool( RECVINFO( m_bAttachSatchel ) ),
- RecvPropBool( RECVINFO( m_bAttachTripmine ) ),
-#else
- SendPropInt( SENDINFO( m_tSlamState ) ),
- SendPropBool( SENDINFO( m_bDetonatorArmed ) ),
- SendPropBool( SENDINFO( m_bNeedDetonatorDraw ) ),
- SendPropBool( SENDINFO( m_bNeedDetonatorHolster ) ),
- SendPropBool( SENDINFO( m_bNeedReload ) ),
- SendPropBool( SENDINFO( m_bClearReload ) ),
- SendPropBool( SENDINFO( m_bThrowSatchel ) ),
- SendPropBool( SENDINFO( m_bAttachSatchel ) ),
- SendPropBool( SENDINFO( m_bAttachTripmine ) ),
-#endif
-END_NETWORK_TABLE()
-
-#ifdef CLIENT_DLL
-
-BEGIN_PREDICTION_DATA( CWeapon_SLAM )
- DEFINE_PRED_FIELD( m_tSlamState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bDetonatorArmed, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bNeedDetonatorDraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bNeedDetonatorHolster, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bNeedReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bClearReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bThrowSatchel, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bAttachSatchel, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
- DEFINE_PRED_FIELD( m_bAttachTripmine, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
-END_PREDICTION_DATA()
-
-#endif
-
-LINK_ENTITY_TO_CLASS( weapon_slam, CWeapon_SLAM );
-PRECACHE_WEAPON_REGISTER(weapon_slam);
-
-#ifndef CLIENT_DLL
-
-BEGIN_DATADESC( CWeapon_SLAM )
-
- DEFINE_FIELD( m_tSlamState, FIELD_INTEGER ),
- DEFINE_FIELD( m_bDetonatorArmed, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bNeedDetonatorDraw, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bNeedDetonatorHolster, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bNeedReload, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bClearReload, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bThrowSatchel, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bAttachSatchel, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_bAttachTripmine, FIELD_BOOLEAN ),
- DEFINE_FIELD( m_flWallSwitchTime, FIELD_TIME ),
-
- // Function Pointers
- DEFINE_FUNCTION( SlamTouch ),
-
-END_DATADESC()
-
-acttable_t CWeapon_SLAM::m_acttable[] =
-{
- { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true },
- { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SLAM, false },
- { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SLAM, false },
- { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SLAM, false },
- { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SLAM, false },
- { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SLAM, false },
- { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SLAM, false },
- { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SLAM, false },
-};
-
-IMPLEMENT_ACTTABLE(CWeapon_SLAM);
-#endif
-
-
-void CWeapon_SLAM::Spawn( )
-{
- BaseClass::Spawn();
-
- Precache( );
-
- FallInit();// get ready to fall down
-
- m_tSlamState = (int)SLAM_SATCHEL_THROW;
- m_flWallSwitchTime = 0;
-
- // Give 1 piece of default ammo when first picked up
- m_iClip2 = 1;
-}
-
-void CWeapon_SLAM::Precache( void )
-{
- BaseClass::Precache();
-
-#ifndef CLIENT_DLL
- UTIL_PrecacheOther( "npc_tripmine" );
- UTIL_PrecacheOther( "npc_satchel" );
-#endif
-
- PrecacheScriptSound( "Weapon_SLAM.TripMineMode" );
- PrecacheScriptSound( "Weapon_SLAM.SatchelDetonate" );
- PrecacheScriptSound( "Weapon_SLAM.SatchelThrow" );
-}
-
-//------------------------------------------------------------------------------
-// Purpose : Override to use slam's pickup touch function
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-void CWeapon_SLAM::SetPickupTouch( void )
-{
- SetTouch(&CWeapon_SLAM::SlamTouch);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override so give correct ammo
-// Input : pOther - the entity that touched me
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::SlamTouch( CBaseEntity *pOther )
-{
-#ifdef GAME_DLL
- CBaseCombatCharacter* pBCC = ToBaseCombatCharacter( pOther );
-
- // Can I even pick stuff up?
- if ( pBCC && !pBCC->IsAllowedToPickupWeapons() )
- return;
-#endif
-
- // ---------------------------------------------------
- // First give weapon to touching entity if allowed
- // ---------------------------------------------------
- BaseClass::DefaultTouch(pOther);
-}
-
-//------------------------------------------------------------------------------
-// Purpose :
-// Input :
-// Output :
-//------------------------------------------------------------------------------
-bool CWeapon_SLAM::Holster( CBaseCombatWeapon *pSwitchingTo )
-{
- SetThink(NULL);
- return BaseClass::Holster(pSwitchingTo);
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: SLAM has no reload, but must call weapon idle to update state
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-bool CWeapon_SLAM::Reload( void )
-{
- WeaponIdle( );
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::PrimaryAttack( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
- if (!pOwner)
- {
- return;
- }
-
- if (pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0)
- {
- return;
- }
-
- switch (m_tSlamState)
- {
- case SLAM_TRIPMINE_READY:
- if (CanAttachSLAM())
- {
- StartTripmineAttach();
- }
- break;
- case SLAM_SATCHEL_THROW:
- StartSatchelThrow();
- break;
- case SLAM_SATCHEL_ATTACH:
- StartSatchelAttach();
- break;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Secondary attack switches between satchel charge and tripmine mode
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::SecondaryAttack( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
- if (!pOwner)
- {
- return;
- }
-
- if (m_bDetonatorArmed)
- {
- StartSatchelDetonate();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::SatchelDetonate()
-{
-#ifndef CLIENT_DLL
- CBaseEntity *pEntity = NULL;
-
- while ((pEntity = gEntList.FindEntityByClassname( pEntity, "npc_satchel" )) != NULL)
- {
- CSatchelCharge *pSatchel = dynamic_cast<CSatchelCharge *>(pEntity);
- if (pSatchel->m_bIsLive && pSatchel->GetThrower() && GetOwner() && pSatchel->GetThrower() == GetOwner())
- {
- //pSatchel->Use( GetOwner(), GetOwner(), USE_ON, 0 );
- //variant_t emptyVariant;
- //pSatchel->AcceptInput( "Explode", NULL, NULL, emptyVariant, 5 );
- g_EventQueue.AddEvent( pSatchel, "Explode", 0.20, GetOwner(), GetOwner() );
- }
- }
-#endif
- // Play sound for pressing the detonator
- EmitSound( "Weapon_SLAM.SatchelDetonate" );
-
- m_bDetonatorArmed = false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Returns true if there are any undetonated charges in the world
-// that belong to this player
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-bool CWeapon_SLAM::AnyUndetonatedCharges(void)
-{
-#ifndef CLIENT_DLL
- CBaseEntity *pEntity = NULL;
-
- while ((pEntity = gEntList.FindEntityByClassname( pEntity, "npc_satchel" )) != NULL)
- {
- CSatchelCharge* pSatchel = dynamic_cast<CSatchelCharge *>(pEntity);
- if (pSatchel->m_bIsLive && pSatchel->GetThrower() && pSatchel->GetThrower() == GetOwner())
- {
- return true;
- }
- }
-#endif
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::StartSatchelDetonate()
-{
-
- if ( GetActivity() != ACT_SLAM_DETONATOR_IDLE && GetActivity() != ACT_SLAM_THROW_IDLE )
- return;
-
- // -----------------------------------------
- // Play detonate animation
- // -----------------------------------------
- if (m_bNeedReload)
- {
- SendWeaponAnim(ACT_SLAM_DETONATOR_DETONATE);
- }
- else if (m_tSlamState == SLAM_SATCHEL_ATTACH)
- {
- SendWeaponAnim(ACT_SLAM_STICKWALL_DETONATE);
- }
- else if (m_tSlamState == SLAM_SATCHEL_THROW)
- {
- SendWeaponAnim(ACT_SLAM_THROW_DETONATE);
- }
- else
- {
- return;
- }
- SatchelDetonate();
-
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::TripmineAttach( void )
-{
- CHL2MP_Player *pOwner = ToHL2MPPlayer( GetOwner() );
- if (!pOwner)
- {
- return;
- }
-
- m_bAttachTripmine = false;
-
- Vector vecSrc, vecAiming;
-
- // Take the eye position and direction
- vecSrc = pOwner->EyePosition();
-
- QAngle angles = pOwner->GetLocalAngles();
-
- AngleVectors( angles, &vecAiming );
-
- trace_t tr;
-
- UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
-
- if (tr.fraction < 1.0)
- {
- CBaseEntity *pEntity = tr.m_pEnt;
- if (pEntity && !(pEntity->GetFlags() & FL_CONVEYOR))
- {
-
-#ifndef CLIENT_DLL
- QAngle angles;
- VectorAngles(tr.plane.normal, angles);
-
- angles.x += 90;
-
- CBaseEntity *pEnt = CBaseEntity::Create( "npc_tripmine", tr.endpos + tr.plane.normal * 3, angles, NULL );
-
- CTripmineGrenade *pMine = (CTripmineGrenade *)pEnt;
- pMine->m_hOwner = GetOwner();
-
-#endif
-
- pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::StartTripmineAttach( void )
-{
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
- if (!pPlayer)
- {
- return;
- }
-
- Vector vecSrc, vecAiming;
-
- // Take the eye position and direction
- vecSrc = pPlayer->EyePosition();
-
- QAngle angles = pPlayer->GetLocalAngles();
-
- AngleVectors( angles, &vecAiming );
-
- trace_t tr;
-
- UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
-
- if (tr.fraction < 1.0)
- {
- // ALERT( at_console, "hit %f\n", tr.flFraction );
-
- CBaseEntity *pEntity = tr.m_pEnt;
- if (pEntity && !(pEntity->GetFlags() & FL_CONVEYOR))
- {
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- // -----------------------------------------
- // Play attach animation
- // -----------------------------------------
-
- if (m_bDetonatorArmed)
- {
- SendWeaponAnim(ACT_SLAM_STICKWALL_ATTACH);
- }
- else
- {
- SendWeaponAnim(ACT_SLAM_TRIPMINE_ATTACH);
- }
-
- m_bNeedReload = true;
- m_bAttachTripmine = true;
- m_bNeedDetonatorDraw = m_bDetonatorArmed;
- }
- else
- {
- // ALERT( at_console, "no deploy\n" );
- }
- }
-
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
-// SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-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() );
-
- Vector vecSrc = pPlayer->WorldSpaceCenter();
- Vector vecFacing = pPlayer->BodyDirection3D( );
- vecSrc = vecSrc + vecFacing * 18.0;
- // BUGBUG: is this because vecSrc is not from Weapon_ShootPosition()???
- vecSrc.z += 24.0f;
-
- Vector vecThrow;
- GetOwner()->GetVelocity( &vecThrow, NULL );
- vecThrow += vecFacing * 500;
-
- // Player may have turned to face a wall during the throw anim in which case
- // we don't want to throw the SLAM into the wall
- if (CanAttachSLAM())
- {
- vecThrow = vecFacing;
- vecSrc = pPlayer->WorldSpaceCenter() + vecFacing * 5.0;
- }
-
- CSatchelCharge *pSatchel = (CSatchelCharge*)Create( "npc_satchel", vecSrc, vec3_angle, GetOwner() );
-
- if ( pSatchel )
- {
- pSatchel->SetThrower( GetOwner() );
- pSatchel->ApplyAbsVelocityImpulse( vecThrow );
- pSatchel->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) );
- pSatchel->m_bIsLive = true;
- pSatchel->m_pMyWeaponSLAM = this;
- }
-
- pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType );
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
-#endif
-
- // Play throw sound
- EmitSound( "Weapon_SLAM.SatchelThrow" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::StartSatchelThrow( void )
-{
- // -----------------------------------------
- // Play throw animation
- // -----------------------------------------
- if (m_bDetonatorArmed)
- {
- SendWeaponAnim(ACT_SLAM_THROW_THROW);
- }
- else
- {
- SendWeaponAnim(ACT_SLAM_THROW_THROW_ND);
- if (!m_bDetonatorArmed)
- {
- m_bDetonatorArmed = true;
- m_bNeedDetonatorDraw = true;
- }
- }
-
- m_bNeedReload = true;
- m_bThrowSatchel = true;
-
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::SatchelAttach( void )
-{
-#ifndef CLIENT_DLL
- CBaseCombatCharacter *pOwner = GetOwner();
- if (!pOwner)
- {
- return;
- }
-
- m_bAttachSatchel = false;
-
- Vector vecSrc = pOwner->Weapon_ShootPosition( );
- Vector vecAiming = pOwner->BodyDirection2D( );
-
- trace_t tr;
-
- UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
-
- if (tr.fraction < 1.0)
- {
- CBaseEntity *pEntity = tr.m_pEnt;
- if (pEntity && !(pEntity->GetFlags() & FL_CONVEYOR))
- {
- QAngle angles;
- VectorAngles(tr.plane.normal, angles);
- angles.y -= 90;
- angles.z -= 90;
- tr.endpos.z -= 6.0f;
-
- CSatchelCharge *pSatchel = (CSatchelCharge*)CBaseEntity::Create( "npc_satchel", tr.endpos + tr.plane.normal * 3, angles, NULL );
- pSatchel->SetMoveType( MOVETYPE_FLY ); // no gravity
- pSatchel->m_bIsAttached = true;
- pSatchel->m_bIsLive = true;
- pSatchel->SetThrower( GetOwner() );
- pSatchel->SetOwnerEntity( ((CBaseEntity*)GetOwner()) );
- pSatchel->m_pMyWeaponSLAM = this;
-
- pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );
- }
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::StartSatchelAttach( void )
-{
-#ifndef CLIENT_DLL
- CBaseCombatCharacter *pOwner = GetOwner();
- if (!pOwner)
- {
- return;
- }
-
- Vector vecSrc = pOwner->Weapon_ShootPosition( );
- Vector vecAiming = pOwner->BodyDirection2D( );
-
- trace_t tr;
-
- UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
-
- if (tr.fraction < 1.0)
- {
- CBaseEntity *pEntity = tr.m_pEnt;
- if (pEntity && !(pEntity->GetFlags() & FL_CONVEYOR))
- {
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( pOwner );
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- // -----------------------------------------
- // Play attach animation
- // -----------------------------------------
- if (m_bDetonatorArmed)
- {
- SendWeaponAnim(ACT_SLAM_STICKWALL_ATTACH);
- }
- else
- {
- SendWeaponAnim(ACT_SLAM_STICKWALL_ND_ATTACH);
- if (!m_bDetonatorArmed)
- {
- m_bDetonatorArmed = true;
- m_bNeedDetonatorDraw = true;
- }
- }
-
- m_bNeedReload = true;
- m_bAttachSatchel = true;
-
- m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
- }
- }
-#endif
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::SetSlamState( int newState )
-{
- // Set set and set idle time so animation gets updated with state change
- m_tSlamState = newState;
- SetWeaponIdleTime( gpGlobals->curtime );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::SLAMThink( void )
-{
- if ( m_flWallSwitchTime > gpGlobals->curtime )
- return;
-
-
- // If not in tripmine mode we need to check to see if we are close to
- // a wall. If we are we go into satchel_attach mode
- CBaseCombatCharacter *pOwner = GetOwner();
-
- if ( (pOwner && pOwner->GetAmmoCount(m_iSecondaryAmmoType) > 0))
- {
- if (CanAttachSLAM())
- {
- if (m_tSlamState == SLAM_SATCHEL_THROW)
- {
- SetSlamState(SLAM_TRIPMINE_READY);
- int iAnim = m_bDetonatorArmed ? ACT_SLAM_THROW_TO_STICKWALL : ACT_SLAM_THROW_TO_TRIPMINE_ND;
- SendWeaponAnim( iAnim );
- m_flWallSwitchTime = gpGlobals->curtime + SequenceDuration();
- m_bNeedReload = false;
- }
- }
- else
- {
- if (m_tSlamState == SLAM_TRIPMINE_READY)
- {
- SetSlamState(SLAM_SATCHEL_THROW);
- int iAnim = m_bDetonatorArmed ? ACT_SLAM_STICKWALL_TO_THROW : ACT_SLAM_TRIPMINE_TO_THROW_ND;
- SendWeaponAnim( iAnim );
- m_flWallSwitchTime = gpGlobals->curtime + SequenceDuration();
- m_bNeedReload = false;
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-bool CWeapon_SLAM::CanAttachSLAM( void )
-{
- CHL2MP_Player *pOwner = ToHL2MPPlayer( GetOwner() );
-
- if (!pOwner)
- {
- return false;
- }
-
- Vector vecSrc, vecAiming;
-
- // Take the eye position and direction
- vecSrc = pOwner->EyePosition();
-
- QAngle angles = pOwner->GetLocalAngles();
-
- AngleVectors( angles, &vecAiming );
-
- trace_t tr;
-
- Vector vecEnd = vecSrc + (vecAiming * 42);
- UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
-
- if (tr.fraction < 1.0)
- {
- // Don't attach to a living creature
- if (tr.m_pEnt)
- {
- CBaseEntity *pEntity = tr.m_pEnt;
- CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
- if (pBCC)
- {
- return false;
- }
- }
- return true;
- }
- else
- {
- return false;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Override so SLAM to so secondary attack when no secondary ammo
-// but satchel is in the world
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::ItemPostFrame( void )
-{
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- if (!pOwner)
- {
- return;
- }
-
- SLAMThink();
-
- if ((pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime))
- {
- SecondaryAttack();
- }
- else if (!m_bNeedReload && (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
- {
- PrimaryAttack();
- }
-
- // -----------------------
- // No buttons down
- // -----------------------
- else
- {
- WeaponIdle( );
- return;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Switch to next best weapon
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::Weapon_Switch( void )
-{
- // Note that we may pick the SLAM again, when we switch
- // weapons, in which case we have to save and restore the
- // detonator armed state.
- // The SLAMs may be about to blow up, but haven't done so yet
- // and the deploy function will find the undetonated charges
- // and we are armed
- bool saveState = m_bDetonatorArmed;
- CBaseCombatCharacter *pOwner = GetOwner();
- pOwner->SwitchToNextBestWeapon( pOwner->GetActiveWeapon() );
- if (pOwner->GetActiveWeapon() == this)
- {
- m_bDetonatorArmed = saveState;
- }
-
-#ifndef CLIENT_DLL
- // If not armed and have no ammo
- if (!m_bDetonatorArmed && pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0)
- {
- pOwner->ClearActiveWeapon();
- }
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-void CWeapon_SLAM::WeaponIdle( void )
-{
- // Ready to switch animations?
- if ( HasWeaponIdleTimeElapsed() )
- {
- // Don't allow throw to attach switch unless in idle
- if (m_bClearReload)
- {
- m_bNeedReload = false;
- m_bClearReload = false;
- }
- CBaseCombatCharacter *pOwner = GetOwner();
- if (!pOwner)
- {
- return;
- }
-
- int iAnim = 0;
-
- if (m_bThrowSatchel)
- {
- SatchelThrow();
- if (m_bDetonatorArmed && !m_bNeedDetonatorDraw)
- {
- iAnim = ACT_SLAM_THROW_THROW2;
- }
- else
- {
- iAnim = ACT_SLAM_THROW_THROW_ND2;
- }
- }
- else if (m_bAttachSatchel)
- {
- SatchelAttach();
- if (m_bDetonatorArmed && !m_bNeedDetonatorDraw)
- {
- iAnim = ACT_SLAM_STICKWALL_ATTACH2;
- }
- else
- {
- iAnim = ACT_SLAM_STICKWALL_ND_ATTACH2;
- }
- }
- else if (m_bAttachTripmine)
- {
- TripmineAttach();
- iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_STICKWALL_ATTACH2 : ACT_SLAM_TRIPMINE_ATTACH2;
- }
- else if ( m_bNeedReload )
- {
- // If owner had ammo draw the correct SLAM type
- if (pOwner->GetAmmoCount(m_iSecondaryAmmoType) > 0)
- {
- switch( m_tSlamState)
- {
- case SLAM_TRIPMINE_READY:
- {
- iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_STICKWALL_DRAW : ACT_SLAM_TRIPMINE_DRAW;
- }
- break;
- case SLAM_SATCHEL_ATTACH:
- {
- if (m_bNeedDetonatorHolster)
- {
- iAnim = ACT_SLAM_STICKWALL_DETONATOR_HOLSTER;
- m_bNeedDetonatorHolster = false;
- }
- else if (m_bDetonatorArmed)
- {
- iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_DETONATOR_STICKWALL_DRAW : ACT_SLAM_STICKWALL_DRAW;
- m_bNeedDetonatorDraw = false;
- }
- else
- {
- iAnim = ACT_SLAM_STICKWALL_ND_DRAW;
- }
- }
- break;
- case SLAM_SATCHEL_THROW:
- {
- if (m_bNeedDetonatorHolster)
- {
- iAnim = ACT_SLAM_THROW_DETONATOR_HOLSTER;
- m_bNeedDetonatorHolster = false;
- }
- else if (m_bDetonatorArmed)
- {
- iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_DETONATOR_THROW_DRAW : ACT_SLAM_THROW_DRAW;
- m_bNeedDetonatorDraw = false;
- }
- else
- {
- iAnim = ACT_SLAM_THROW_ND_DRAW;
- }
- }
- break;
- }
- m_bClearReload = true;
- }
- // If no ammo and armed, idle with only the detonator
- else if (m_bDetonatorArmed)
- {
- iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_DETONATOR_DRAW : ACT_SLAM_DETONATOR_IDLE;
- m_bNeedDetonatorDraw = false;
- }
- else
- {
-#ifndef CLIENT_DLL
- pOwner->Weapon_Drop( this );
- UTIL_Remove(this);
-#endif
- }
- }
- else if (pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0)
- {
-#ifndef CLIENT_DLL
- pOwner->Weapon_Drop( this );
- UTIL_Remove(this);
-#endif
- }
-
- // If I don't need to reload just do the appropriate idle
- else
- {
- switch( m_tSlamState)
- {
- case SLAM_TRIPMINE_READY:
- {
- iAnim = m_bDetonatorArmed ? ACT_SLAM_STICKWALL_IDLE : ACT_SLAM_TRIPMINE_IDLE;
- m_flWallSwitchTime = 0;
- }
- break;
- case SLAM_SATCHEL_THROW:
- {
- if (m_bNeedDetonatorHolster)
- {
- iAnim = ACT_SLAM_THROW_DETONATOR_HOLSTER;
- m_bNeedDetonatorHolster = false;
- }
- else
- {
- iAnim = m_bDetonatorArmed ? ACT_SLAM_THROW_IDLE : ACT_SLAM_THROW_ND_IDLE;
- m_flWallSwitchTime = 0;
- }
- }
- break;
- case SLAM_SATCHEL_ATTACH:
- {
- if (m_bNeedDetonatorHolster)
- {
- iAnim = ACT_SLAM_STICKWALL_DETONATOR_HOLSTER;
- m_bNeedDetonatorHolster = false;
- }
- else
- {
- iAnim = m_bDetonatorArmed ? ACT_SLAM_STICKWALL_IDLE : ACT_SLAM_TRIPMINE_IDLE;
- m_flWallSwitchTime = 0;
- }
- }
- break;
- }
- }
- SendWeaponAnim( iAnim );
- }
-}
-
-bool CWeapon_SLAM::Deploy( void )
-{
- CBaseCombatCharacter *pOwner = GetOwner();
- if (!pOwner)
- {
- return false;
- }
-
- m_bDetonatorArmed = AnyUndetonatedCharges();
-
-
- SetModel( GetViewModel() );
-
- m_tSlamState = (int)SLAM_SATCHEL_THROW;
-
- // ------------------------------
- // Pick the right draw animation
- // ------------------------------
- int iActivity;
-
- // If detonator is already armed
- m_bNeedReload = false;
- if (m_bDetonatorArmed)
- {
- if (pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0)
- {
- iActivity = ACT_SLAM_DETONATOR_DRAW;
- m_bNeedReload = true;
- }
- else if (CanAttachSLAM())
- {
- iActivity = ACT_SLAM_DETONATOR_STICKWALL_DRAW;
- SetSlamState(SLAM_TRIPMINE_READY);
- }
- else
- {
- iActivity = ACT_SLAM_DETONATOR_THROW_DRAW;
- SetSlamState(SLAM_SATCHEL_THROW);
- }
- }
- else
- {
- if (CanAttachSLAM())
- {
- iActivity = ACT_SLAM_TRIPMINE_DRAW;
- SetSlamState(SLAM_TRIPMINE_READY);
- }
- else
- {
- iActivity = ACT_SLAM_THROW_ND_DRAW;
- SetSlamState(SLAM_SATCHEL_THROW);
- }
- }
-
- return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), iActivity, (char*)GetAnimPrefix() );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Constructor
-// Input :
-// Output :
-//-----------------------------------------------------------------------------
-CWeapon_SLAM::CWeapon_SLAM(void)
-{
- m_tSlamState = (int)SLAM_SATCHEL_THROW;
- m_bDetonatorArmed = false;
- m_bNeedReload = true;
- m_bClearReload = false;
- m_bThrowSatchel = false;
- m_bAttachSatchel = false;
- m_bAttachTripmine = false;
- m_bNeedDetonatorDraw = false;
- m_bNeedDetonatorHolster = false;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "gamerules.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+#include "engine/IEngineSound.h"
+
+#if defined( CLIENT_DLL )
+ #include "c_hl2mp_player.h"
+#else
+ #include "hl2mp_player.h"
+ #include "grenade_tripmine.h"
+ #include "grenade_satchel.h"
+ #include "entitylist.h"
+ #include "eventqueue.h"
+#endif
+
+#include "hl2mp/weapon_slam.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define SLAM_PRIMARY_VOLUME 450
+
+IMPLEMENT_NETWORKCLASS_ALIASED( Weapon_SLAM, DT_Weapon_SLAM )
+
+BEGIN_NETWORK_TABLE( CWeapon_SLAM, DT_Weapon_SLAM )
+#ifdef CLIENT_DLL
+ RecvPropInt( RECVINFO( m_tSlamState ) ),
+ RecvPropBool( RECVINFO( m_bDetonatorArmed ) ),
+ RecvPropBool( RECVINFO( m_bNeedDetonatorDraw ) ),
+ RecvPropBool( RECVINFO( m_bNeedDetonatorHolster ) ),
+ RecvPropBool( RECVINFO( m_bNeedReload ) ),
+ RecvPropBool( RECVINFO( m_bClearReload ) ),
+ RecvPropBool( RECVINFO( m_bThrowSatchel ) ),
+ RecvPropBool( RECVINFO( m_bAttachSatchel ) ),
+ RecvPropBool( RECVINFO( m_bAttachTripmine ) ),
+#else
+ SendPropInt( SENDINFO( m_tSlamState ) ),
+ SendPropBool( SENDINFO( m_bDetonatorArmed ) ),
+ SendPropBool( SENDINFO( m_bNeedDetonatorDraw ) ),
+ SendPropBool( SENDINFO( m_bNeedDetonatorHolster ) ),
+ SendPropBool( SENDINFO( m_bNeedReload ) ),
+ SendPropBool( SENDINFO( m_bClearReload ) ),
+ SendPropBool( SENDINFO( m_bThrowSatchel ) ),
+ SendPropBool( SENDINFO( m_bAttachSatchel ) ),
+ SendPropBool( SENDINFO( m_bAttachTripmine ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+
+BEGIN_PREDICTION_DATA( CWeapon_SLAM )
+ DEFINE_PRED_FIELD( m_tSlamState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bDetonatorArmed, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bNeedDetonatorDraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bNeedDetonatorHolster, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bNeedReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bClearReload, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bThrowSatchel, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bAttachSatchel, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bAttachTripmine, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_slam, CWeapon_SLAM );
+PRECACHE_WEAPON_REGISTER(weapon_slam);
+
+#ifndef CLIENT_DLL
+
+BEGIN_DATADESC( CWeapon_SLAM )
+
+ DEFINE_FIELD( m_tSlamState, FIELD_INTEGER ),
+ DEFINE_FIELD( m_bDetonatorArmed, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bNeedDetonatorDraw, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bNeedDetonatorHolster, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bNeedReload, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bClearReload, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bThrowSatchel, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bAttachSatchel, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bAttachTripmine, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_flWallSwitchTime, FIELD_TIME ),
+
+ // Function Pointers
+ DEFINE_FUNCTION( SlamTouch ),
+
+END_DATADESC()
+
+acttable_t CWeapon_SLAM::m_acttable[] =
+{
+ { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true },
+ { ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SLAM, false },
+ { ACT_HL2MP_RUN, ACT_HL2MP_RUN_SLAM, false },
+ { ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SLAM, false },
+ { ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SLAM, false },
+ { ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SLAM, false },
+ { ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SLAM, false },
+ { ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SLAM, false },
+};
+
+IMPLEMENT_ACTTABLE(CWeapon_SLAM);
+#endif
+
+
+void CWeapon_SLAM::Spawn( )
+{
+ BaseClass::Spawn();
+
+ Precache( );
+
+ FallInit();// get ready to fall down
+
+ m_tSlamState = (int)SLAM_SATCHEL_THROW;
+ m_flWallSwitchTime = 0;
+
+ // Give 1 piece of default ammo when first picked up
+ m_iClip2 = 1;
+}
+
+void CWeapon_SLAM::Precache( void )
+{
+ BaseClass::Precache();
+
+#ifndef CLIENT_DLL
+ UTIL_PrecacheOther( "npc_tripmine" );
+ UTIL_PrecacheOther( "npc_satchel" );
+#endif
+
+ PrecacheScriptSound( "Weapon_SLAM.TripMineMode" );
+ PrecacheScriptSound( "Weapon_SLAM.SatchelDetonate" );
+ PrecacheScriptSound( "Weapon_SLAM.SatchelThrow" );
+}
+
+//------------------------------------------------------------------------------
+// Purpose : Override to use slam's pickup touch function
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+void CWeapon_SLAM::SetPickupTouch( void )
+{
+ SetTouch(&CWeapon_SLAM::SlamTouch);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override so give correct ammo
+// Input : pOther - the entity that touched me
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::SlamTouch( CBaseEntity *pOther )
+{
+#ifdef GAME_DLL
+ CBaseCombatCharacter* pBCC = ToBaseCombatCharacter( pOther );
+
+ // Can I even pick stuff up?
+ if ( pBCC && !pBCC->IsAllowedToPickupWeapons() )
+ return;
+#endif
+
+ // ---------------------------------------------------
+ // First give weapon to touching entity if allowed
+ // ---------------------------------------------------
+ BaseClass::DefaultTouch(pOther);
+}
+
+//------------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//------------------------------------------------------------------------------
+bool CWeapon_SLAM::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ SetThink(NULL);
+ return BaseClass::Holster(pSwitchingTo);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: SLAM has no reload, but must call weapon idle to update state
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool CWeapon_SLAM::Reload( void )
+{
+ WeaponIdle( );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::PrimaryAttack( void )
+{
+ CBaseCombatCharacter *pOwner = GetOwner();
+ if (!pOwner)
+ {
+ return;
+ }
+
+ if (pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0)
+ {
+ return;
+ }
+
+ switch (m_tSlamState)
+ {
+ case SLAM_TRIPMINE_READY:
+ if (CanAttachSLAM())
+ {
+ StartTripmineAttach();
+ }
+ break;
+ case SLAM_SATCHEL_THROW:
+ StartSatchelThrow();
+ break;
+ case SLAM_SATCHEL_ATTACH:
+ StartSatchelAttach();
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Secondary attack switches between satchel charge and tripmine mode
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::SecondaryAttack( void )
+{
+ CBaseCombatCharacter *pOwner = GetOwner();
+ if (!pOwner)
+ {
+ return;
+ }
+
+ if (m_bDetonatorArmed)
+ {
+ StartSatchelDetonate();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::SatchelDetonate()
+{
+#ifndef CLIENT_DLL
+ CBaseEntity *pEntity = NULL;
+
+ while ((pEntity = gEntList.FindEntityByClassname( pEntity, "npc_satchel" )) != NULL)
+ {
+ CSatchelCharge *pSatchel = dynamic_cast<CSatchelCharge *>(pEntity);
+ if (pSatchel->m_bIsLive && pSatchel->GetThrower() && GetOwner() && pSatchel->GetThrower() == GetOwner())
+ {
+ //pSatchel->Use( GetOwner(), GetOwner(), USE_ON, 0 );
+ //variant_t emptyVariant;
+ //pSatchel->AcceptInput( "Explode", NULL, NULL, emptyVariant, 5 );
+ g_EventQueue.AddEvent( pSatchel, "Explode", 0.20, GetOwner(), GetOwner() );
+ }
+ }
+#endif
+ // Play sound for pressing the detonator
+ EmitSound( "Weapon_SLAM.SatchelDetonate" );
+
+ m_bDetonatorArmed = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if there are any undetonated charges in the world
+// that belong to this player
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool CWeapon_SLAM::AnyUndetonatedCharges(void)
+{
+#ifndef CLIENT_DLL
+ CBaseEntity *pEntity = NULL;
+
+ while ((pEntity = gEntList.FindEntityByClassname( pEntity, "npc_satchel" )) != NULL)
+ {
+ CSatchelCharge* pSatchel = dynamic_cast<CSatchelCharge *>(pEntity);
+ if (pSatchel->m_bIsLive && pSatchel->GetThrower() && pSatchel->GetThrower() == GetOwner())
+ {
+ return true;
+ }
+ }
+#endif
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::StartSatchelDetonate()
+{
+
+ if ( GetActivity() != ACT_SLAM_DETONATOR_IDLE && GetActivity() != ACT_SLAM_THROW_IDLE )
+ return;
+
+ // -----------------------------------------
+ // Play detonate animation
+ // -----------------------------------------
+ if (m_bNeedReload)
+ {
+ SendWeaponAnim(ACT_SLAM_DETONATOR_DETONATE);
+ }
+ else if (m_tSlamState == SLAM_SATCHEL_ATTACH)
+ {
+ SendWeaponAnim(ACT_SLAM_STICKWALL_DETONATE);
+ }
+ else if (m_tSlamState == SLAM_SATCHEL_THROW)
+ {
+ SendWeaponAnim(ACT_SLAM_THROW_DETONATE);
+ }
+ else
+ {
+ return;
+ }
+ SatchelDetonate();
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::TripmineAttach( void )
+{
+ CHL2MP_Player *pOwner = ToHL2MPPlayer( GetOwner() );
+ if (!pOwner)
+ {
+ return;
+ }
+
+ m_bAttachTripmine = false;
+
+ Vector vecSrc, vecAiming;
+
+ // Take the eye position and direction
+ vecSrc = pOwner->EyePosition();
+
+ QAngle angles = pOwner->GetLocalAngles();
+
+ AngleVectors( angles, &vecAiming );
+
+ trace_t tr;
+
+ UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ if (tr.fraction < 1.0)
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if (pEntity && !(pEntity->GetFlags() & FL_CONVEYOR))
+ {
+
+#ifndef CLIENT_DLL
+ QAngle angles;
+ VectorAngles(tr.plane.normal, angles);
+
+ angles.x += 90;
+
+ CBaseEntity *pEnt = CBaseEntity::Create( "npc_tripmine", tr.endpos + tr.plane.normal * 3, angles, NULL );
+
+ CTripmineGrenade *pMine = (CTripmineGrenade *)pEnt;
+ pMine->m_hOwner = GetOwner();
+
+#endif
+
+ pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::StartTripmineAttach( void )
+{
+ // Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if (!pPlayer)
+ {
+ return;
+ }
+
+ Vector vecSrc, vecAiming;
+
+ // Take the eye position and direction
+ vecSrc = pPlayer->EyePosition();
+
+ QAngle angles = pPlayer->GetLocalAngles();
+
+ AngleVectors( angles, &vecAiming );
+
+ trace_t tr;
+
+ UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
+
+ if (tr.fraction < 1.0)
+ {
+ // ALERT( at_console, "hit %f\n", tr.flFraction );
+
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if (pEntity && !(pEntity->GetFlags() & FL_CONVEYOR))
+ {
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ // -----------------------------------------
+ // Play attach animation
+ // -----------------------------------------
+
+ if (m_bDetonatorArmed)
+ {
+ SendWeaponAnim(ACT_SLAM_STICKWALL_ATTACH);
+ }
+ else
+ {
+ SendWeaponAnim(ACT_SLAM_TRIPMINE_ATTACH);
+ }
+
+ m_bNeedReload = true;
+ m_bAttachTripmine = true;
+ m_bNeedDetonatorDraw = m_bDetonatorArmed;
+ }
+ else
+ {
+ // ALERT( at_console, "no deploy\n" );
+ }
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
+// SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+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() );
+
+ Vector vecSrc = pPlayer->WorldSpaceCenter();
+ Vector vecFacing = pPlayer->BodyDirection3D( );
+ vecSrc = vecSrc + vecFacing * 18.0;
+ // BUGBUG: is this because vecSrc is not from Weapon_ShootPosition()???
+ vecSrc.z += 24.0f;
+
+ Vector vecThrow;
+ GetOwner()->GetVelocity( &vecThrow, NULL );
+ vecThrow += vecFacing * 500;
+
+ // Player may have turned to face a wall during the throw anim in which case
+ // we don't want to throw the SLAM into the wall
+ if (CanAttachSLAM())
+ {
+ vecThrow = vecFacing;
+ vecSrc = pPlayer->WorldSpaceCenter() + vecFacing * 5.0;
+ }
+
+ CSatchelCharge *pSatchel = (CSatchelCharge*)Create( "npc_satchel", vecSrc, vec3_angle, GetOwner() );
+
+ if ( pSatchel )
+ {
+ pSatchel->SetThrower( GetOwner() );
+ pSatchel->ApplyAbsVelocityImpulse( vecThrow );
+ pSatchel->SetLocalAngularVelocity( QAngle( 0, 400, 0 ) );
+ pSatchel->m_bIsLive = true;
+ pSatchel->m_pMyWeaponSLAM = this;
+ }
+
+ pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType );
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+#endif
+
+ // Play throw sound
+ EmitSound( "Weapon_SLAM.SatchelThrow" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::StartSatchelThrow( void )
+{
+ // -----------------------------------------
+ // Play throw animation
+ // -----------------------------------------
+ if (m_bDetonatorArmed)
+ {
+ SendWeaponAnim(ACT_SLAM_THROW_THROW);
+ }
+ else
+ {
+ SendWeaponAnim(ACT_SLAM_THROW_THROW_ND);
+ if (!m_bDetonatorArmed)
+ {
+ m_bDetonatorArmed = true;
+ m_bNeedDetonatorDraw = true;
+ }
+ }
+
+ m_bNeedReload = true;
+ m_bThrowSatchel = true;
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::SatchelAttach( void )
+{
+#ifndef CLIENT_DLL
+ CBaseCombatCharacter *pOwner = GetOwner();
+ if (!pOwner)
+ {
+ return;
+ }
+
+ m_bAttachSatchel = false;
+
+ Vector vecSrc = pOwner->Weapon_ShootPosition( );
+ Vector vecAiming = pOwner->BodyDirection2D( );
+
+ trace_t tr;
+
+ UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ if (tr.fraction < 1.0)
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if (pEntity && !(pEntity->GetFlags() & FL_CONVEYOR))
+ {
+ QAngle angles;
+ VectorAngles(tr.plane.normal, angles);
+ angles.y -= 90;
+ angles.z -= 90;
+ tr.endpos.z -= 6.0f;
+
+ CSatchelCharge *pSatchel = (CSatchelCharge*)CBaseEntity::Create( "npc_satchel", tr.endpos + tr.plane.normal * 3, angles, NULL );
+ pSatchel->SetMoveType( MOVETYPE_FLY ); // no gravity
+ pSatchel->m_bIsAttached = true;
+ pSatchel->m_bIsLive = true;
+ pSatchel->SetThrower( GetOwner() );
+ pSatchel->SetOwnerEntity( ((CBaseEntity*)GetOwner()) );
+ pSatchel->m_pMyWeaponSLAM = this;
+
+ pOwner->RemoveAmmo( 1, m_iSecondaryAmmoType );
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::StartSatchelAttach( void )
+{
+#ifndef CLIENT_DLL
+ CBaseCombatCharacter *pOwner = GetOwner();
+ if (!pOwner)
+ {
+ return;
+ }
+
+ Vector vecSrc = pOwner->Weapon_ShootPosition( );
+ Vector vecAiming = pOwner->BodyDirection2D( );
+
+ trace_t tr;
+
+ UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ if (tr.fraction < 1.0)
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if (pEntity && !(pEntity->GetFlags() & FL_CONVEYOR))
+ {
+ // Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = ToBasePlayer( pOwner );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ // -----------------------------------------
+ // Play attach animation
+ // -----------------------------------------
+ if (m_bDetonatorArmed)
+ {
+ SendWeaponAnim(ACT_SLAM_STICKWALL_ATTACH);
+ }
+ else
+ {
+ SendWeaponAnim(ACT_SLAM_STICKWALL_ND_ATTACH);
+ if (!m_bDetonatorArmed)
+ {
+ m_bDetonatorArmed = true;
+ m_bNeedDetonatorDraw = true;
+ }
+ }
+
+ m_bNeedReload = true;
+ m_bAttachSatchel = true;
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::SetSlamState( int newState )
+{
+ // Set set and set idle time so animation gets updated with state change
+ m_tSlamState = newState;
+ SetWeaponIdleTime( gpGlobals->curtime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::SLAMThink( void )
+{
+ if ( m_flWallSwitchTime > gpGlobals->curtime )
+ return;
+
+
+ // If not in tripmine mode we need to check to see if we are close to
+ // a wall. If we are we go into satchel_attach mode
+ CBaseCombatCharacter *pOwner = GetOwner();
+
+ if ( (pOwner && pOwner->GetAmmoCount(m_iSecondaryAmmoType) > 0))
+ {
+ if (CanAttachSLAM())
+ {
+ if (m_tSlamState == SLAM_SATCHEL_THROW)
+ {
+ SetSlamState(SLAM_TRIPMINE_READY);
+ int iAnim = m_bDetonatorArmed ? ACT_SLAM_THROW_TO_STICKWALL : ACT_SLAM_THROW_TO_TRIPMINE_ND;
+ SendWeaponAnim( iAnim );
+ m_flWallSwitchTime = gpGlobals->curtime + SequenceDuration();
+ m_bNeedReload = false;
+ }
+ }
+ else
+ {
+ if (m_tSlamState == SLAM_TRIPMINE_READY)
+ {
+ SetSlamState(SLAM_SATCHEL_THROW);
+ int iAnim = m_bDetonatorArmed ? ACT_SLAM_STICKWALL_TO_THROW : ACT_SLAM_TRIPMINE_TO_THROW_ND;
+ SendWeaponAnim( iAnim );
+ m_flWallSwitchTime = gpGlobals->curtime + SequenceDuration();
+ m_bNeedReload = false;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+bool CWeapon_SLAM::CanAttachSLAM( void )
+{
+ CHL2MP_Player *pOwner = ToHL2MPPlayer( GetOwner() );
+
+ if (!pOwner)
+ {
+ return false;
+ }
+
+ Vector vecSrc, vecAiming;
+
+ // Take the eye position and direction
+ vecSrc = pOwner->EyePosition();
+
+ QAngle angles = pOwner->GetLocalAngles();
+
+ AngleVectors( angles, &vecAiming );
+
+ trace_t tr;
+
+ Vector vecEnd = vecSrc + (vecAiming * 42);
+ UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
+
+ if (tr.fraction < 1.0)
+ {
+ // Don't attach to a living creature
+ if (tr.m_pEnt)
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity );
+ if (pBCC)
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override so SLAM to so secondary attack when no secondary ammo
+// but satchel is in the world
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::ItemPostFrame( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if (!pOwner)
+ {
+ return;
+ }
+
+ SLAMThink();
+
+ if ((pOwner->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime))
+ {
+ SecondaryAttack();
+ }
+ else if (!m_bNeedReload && (pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime))
+ {
+ PrimaryAttack();
+ }
+
+ // -----------------------
+ // No buttons down
+ // -----------------------
+ else
+ {
+ WeaponIdle( );
+ return;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Switch to next best weapon
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::Weapon_Switch( void )
+{
+ // Note that we may pick the SLAM again, when we switch
+ // weapons, in which case we have to save and restore the
+ // detonator armed state.
+ // The SLAMs may be about to blow up, but haven't done so yet
+ // and the deploy function will find the undetonated charges
+ // and we are armed
+ bool saveState = m_bDetonatorArmed;
+ CBaseCombatCharacter *pOwner = GetOwner();
+ pOwner->SwitchToNextBestWeapon( pOwner->GetActiveWeapon() );
+ if (pOwner->GetActiveWeapon() == this)
+ {
+ m_bDetonatorArmed = saveState;
+ }
+
+#ifndef CLIENT_DLL
+ // If not armed and have no ammo
+ if (!m_bDetonatorArmed && pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0)
+ {
+ pOwner->ClearActiveWeapon();
+ }
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CWeapon_SLAM::WeaponIdle( void )
+{
+ // Ready to switch animations?
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ // Don't allow throw to attach switch unless in idle
+ if (m_bClearReload)
+ {
+ m_bNeedReload = false;
+ m_bClearReload = false;
+ }
+ CBaseCombatCharacter *pOwner = GetOwner();
+ if (!pOwner)
+ {
+ return;
+ }
+
+ int iAnim = 0;
+
+ if (m_bThrowSatchel)
+ {
+ SatchelThrow();
+ if (m_bDetonatorArmed && !m_bNeedDetonatorDraw)
+ {
+ iAnim = ACT_SLAM_THROW_THROW2;
+ }
+ else
+ {
+ iAnim = ACT_SLAM_THROW_THROW_ND2;
+ }
+ }
+ else if (m_bAttachSatchel)
+ {
+ SatchelAttach();
+ if (m_bDetonatorArmed && !m_bNeedDetonatorDraw)
+ {
+ iAnim = ACT_SLAM_STICKWALL_ATTACH2;
+ }
+ else
+ {
+ iAnim = ACT_SLAM_STICKWALL_ND_ATTACH2;
+ }
+ }
+ else if (m_bAttachTripmine)
+ {
+ TripmineAttach();
+ iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_STICKWALL_ATTACH2 : ACT_SLAM_TRIPMINE_ATTACH2;
+ }
+ else if ( m_bNeedReload )
+ {
+ // If owner had ammo draw the correct SLAM type
+ if (pOwner->GetAmmoCount(m_iSecondaryAmmoType) > 0)
+ {
+ switch( m_tSlamState)
+ {
+ case SLAM_TRIPMINE_READY:
+ {
+ iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_STICKWALL_DRAW : ACT_SLAM_TRIPMINE_DRAW;
+ }
+ break;
+ case SLAM_SATCHEL_ATTACH:
+ {
+ if (m_bNeedDetonatorHolster)
+ {
+ iAnim = ACT_SLAM_STICKWALL_DETONATOR_HOLSTER;
+ m_bNeedDetonatorHolster = false;
+ }
+ else if (m_bDetonatorArmed)
+ {
+ iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_DETONATOR_STICKWALL_DRAW : ACT_SLAM_STICKWALL_DRAW;
+ m_bNeedDetonatorDraw = false;
+ }
+ else
+ {
+ iAnim = ACT_SLAM_STICKWALL_ND_DRAW;
+ }
+ }
+ break;
+ case SLAM_SATCHEL_THROW:
+ {
+ if (m_bNeedDetonatorHolster)
+ {
+ iAnim = ACT_SLAM_THROW_DETONATOR_HOLSTER;
+ m_bNeedDetonatorHolster = false;
+ }
+ else if (m_bDetonatorArmed)
+ {
+ iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_DETONATOR_THROW_DRAW : ACT_SLAM_THROW_DRAW;
+ m_bNeedDetonatorDraw = false;
+ }
+ else
+ {
+ iAnim = ACT_SLAM_THROW_ND_DRAW;
+ }
+ }
+ break;
+ }
+ m_bClearReload = true;
+ }
+ // If no ammo and armed, idle with only the detonator
+ else if (m_bDetonatorArmed)
+ {
+ iAnim = m_bNeedDetonatorDraw ? ACT_SLAM_DETONATOR_DRAW : ACT_SLAM_DETONATOR_IDLE;
+ m_bNeedDetonatorDraw = false;
+ }
+ else
+ {
+#ifndef CLIENT_DLL
+ pOwner->Weapon_Drop( this );
+ UTIL_Remove(this);
+#endif
+ }
+ }
+ else if (pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0)
+ {
+#ifndef CLIENT_DLL
+ pOwner->Weapon_Drop( this );
+ UTIL_Remove(this);
+#endif
+ }
+
+ // If I don't need to reload just do the appropriate idle
+ else
+ {
+ switch( m_tSlamState)
+ {
+ case SLAM_TRIPMINE_READY:
+ {
+ iAnim = m_bDetonatorArmed ? ACT_SLAM_STICKWALL_IDLE : ACT_SLAM_TRIPMINE_IDLE;
+ m_flWallSwitchTime = 0;
+ }
+ break;
+ case SLAM_SATCHEL_THROW:
+ {
+ if (m_bNeedDetonatorHolster)
+ {
+ iAnim = ACT_SLAM_THROW_DETONATOR_HOLSTER;
+ m_bNeedDetonatorHolster = false;
+ }
+ else
+ {
+ iAnim = m_bDetonatorArmed ? ACT_SLAM_THROW_IDLE : ACT_SLAM_THROW_ND_IDLE;
+ m_flWallSwitchTime = 0;
+ }
+ }
+ break;
+ case SLAM_SATCHEL_ATTACH:
+ {
+ if (m_bNeedDetonatorHolster)
+ {
+ iAnim = ACT_SLAM_STICKWALL_DETONATOR_HOLSTER;
+ m_bNeedDetonatorHolster = false;
+ }
+ else
+ {
+ iAnim = m_bDetonatorArmed ? ACT_SLAM_STICKWALL_IDLE : ACT_SLAM_TRIPMINE_IDLE;
+ m_flWallSwitchTime = 0;
+ }
+ }
+ break;
+ }
+ }
+ SendWeaponAnim( iAnim );
+ }
+}
+
+bool CWeapon_SLAM::Deploy( void )
+{
+ CBaseCombatCharacter *pOwner = GetOwner();
+ if (!pOwner)
+ {
+ return false;
+ }
+
+ m_bDetonatorArmed = AnyUndetonatedCharges();
+
+
+ SetModel( GetViewModel() );
+
+ m_tSlamState = (int)SLAM_SATCHEL_THROW;
+
+ // ------------------------------
+ // Pick the right draw animation
+ // ------------------------------
+ int iActivity;
+
+ // If detonator is already armed
+ m_bNeedReload = false;
+ if (m_bDetonatorArmed)
+ {
+ if (pOwner->GetAmmoCount(m_iSecondaryAmmoType) <= 0)
+ {
+ iActivity = ACT_SLAM_DETONATOR_DRAW;
+ m_bNeedReload = true;
+ }
+ else if (CanAttachSLAM())
+ {
+ iActivity = ACT_SLAM_DETONATOR_STICKWALL_DRAW;
+ SetSlamState(SLAM_TRIPMINE_READY);
+ }
+ else
+ {
+ iActivity = ACT_SLAM_DETONATOR_THROW_DRAW;
+ SetSlamState(SLAM_SATCHEL_THROW);
+ }
+ }
+ else
+ {
+ if (CanAttachSLAM())
+ {
+ iActivity = ACT_SLAM_TRIPMINE_DRAW;
+ SetSlamState(SLAM_TRIPMINE_READY);
+ }
+ else
+ {
+ iActivity = ACT_SLAM_THROW_ND_DRAW;
+ SetSlamState(SLAM_SATCHEL_THROW);
+ }
+ }
+
+ return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), iActivity, (char*)GetAnimPrefix() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+CWeapon_SLAM::CWeapon_SLAM(void)
+{
+ m_tSlamState = (int)SLAM_SATCHEL_THROW;
+ m_bDetonatorArmed = false;
+ m_bNeedReload = true;
+ m_bClearReload = false;
+ m_bThrowSatchel = false;
+ m_bAttachSatchel = false;
+ m_bAttachTripmine = false;
+ m_bNeedDetonatorDraw = false;
+ m_bNeedDetonatorHolster = false;
+}
diff --git a/mp/src/game/shared/hl2mp/weapon_slam.h b/mp/src/game/shared/hl2mp/weapon_slam.h
index cbf5b8be..0b59bed7 100644
--- a/mp/src/game/shared/hl2mp/weapon_slam.h
+++ b/mp/src/game/shared/hl2mp/weapon_slam.h
@@ -1,91 +1,91 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: SLAM
-//
-// $Workfile: $
-// $Date: $
-//
-//-----------------------------------------------------------------------------
-// $Log: $
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#ifndef WEAPONSLAM_H
-#define WEAPONSLAM_H
-
-#include "basegrenade_shared.h"
-#include "weapon_hl2mpbasehlmpcombatweapon.h"
-
-enum
-{
- SLAM_TRIPMINE_READY,
- SLAM_SATCHEL_THROW,
- SLAM_SATCHEL_ATTACH,
-};
-
-#ifdef CLIENT_DLL
-#define CWeapon_SLAM C_Weapon_SLAM
-#endif
-
-class CWeapon_SLAM : public CBaseHL2MPCombatWeapon
-{
-public:
- DECLARE_CLASS( CWeapon_SLAM, CBaseHL2MPCombatWeapon );
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
- CNetworkVar( int, m_tSlamState );
- CNetworkVar( bool, m_bDetonatorArmed );
- CNetworkVar( bool, m_bNeedDetonatorDraw);
- CNetworkVar( bool, m_bNeedDetonatorHolster);
- CNetworkVar( bool, m_bNeedReload);
- CNetworkVar( bool, m_bClearReload);
- CNetworkVar( bool, m_bThrowSatchel);
- CNetworkVar( bool, m_bAttachSatchel);
- CNetworkVar( bool, m_bAttachTripmine);
- float m_flWallSwitchTime;
-
- void Spawn( void );
- void Precache( void );
-
- void PrimaryAttack( void );
- void SecondaryAttack( void );
- void WeaponIdle( void );
- void Weapon_Switch( void );
- void SLAMThink( void );
-
- void SetPickupTouch( void );
- void SlamTouch( CBaseEntity *pOther ); // default weapon touch
- void ItemPostFrame( void );
- bool Reload( void );
- void SetSlamState( int newState );
- bool CanAttachSLAM(void); // In position where can attach SLAM?
- bool AnyUndetonatedCharges(void);
- void StartTripmineAttach( void );
- void TripmineAttach( void );
-
- void StartSatchelDetonate( void );
- void SatchelDetonate( void );
- void StartSatchelThrow( void );
- void StartSatchelAttach( void );
- void SatchelThrow( void );
- void SatchelAttach( void );
- bool Deploy( void );
- bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
-
-
- CWeapon_SLAM();
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
- DECLARE_DATADESC();
-#endif
-
-private:
- CWeapon_SLAM( const CWeapon_SLAM & );
-};
-
-
-#endif //WEAPONSLAM_H
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: SLAM
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPONSLAM_H
+#define WEAPONSLAM_H
+
+#include "basegrenade_shared.h"
+#include "weapon_hl2mpbasehlmpcombatweapon.h"
+
+enum
+{
+ SLAM_TRIPMINE_READY,
+ SLAM_SATCHEL_THROW,
+ SLAM_SATCHEL_ATTACH,
+};
+
+#ifdef CLIENT_DLL
+#define CWeapon_SLAM C_Weapon_SLAM
+#endif
+
+class CWeapon_SLAM : public CBaseHL2MPCombatWeapon
+{
+public:
+ DECLARE_CLASS( CWeapon_SLAM, CBaseHL2MPCombatWeapon );
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CNetworkVar( int, m_tSlamState );
+ CNetworkVar( bool, m_bDetonatorArmed );
+ CNetworkVar( bool, m_bNeedDetonatorDraw);
+ CNetworkVar( bool, m_bNeedDetonatorHolster);
+ CNetworkVar( bool, m_bNeedReload);
+ CNetworkVar( bool, m_bClearReload);
+ CNetworkVar( bool, m_bThrowSatchel);
+ CNetworkVar( bool, m_bAttachSatchel);
+ CNetworkVar( bool, m_bAttachTripmine);
+ float m_flWallSwitchTime;
+
+ void Spawn( void );
+ void Precache( void );
+
+ void PrimaryAttack( void );
+ void SecondaryAttack( void );
+ void WeaponIdle( void );
+ void Weapon_Switch( void );
+ void SLAMThink( void );
+
+ void SetPickupTouch( void );
+ void SlamTouch( CBaseEntity *pOther ); // default weapon touch
+ void ItemPostFrame( void );
+ bool Reload( void );
+ void SetSlamState( int newState );
+ bool CanAttachSLAM(void); // In position where can attach SLAM?
+ bool AnyUndetonatedCharges(void);
+ void StartTripmineAttach( void );
+ void TripmineAttach( void );
+
+ void StartSatchelDetonate( void );
+ void SatchelDetonate( void );
+ void StartSatchelThrow( void );
+ void StartSatchelAttach( void );
+ void SatchelThrow( void );
+ void SatchelAttach( void );
+ bool Deploy( void );
+ bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+
+
+ CWeapon_SLAM();
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+ DECLARE_DATADESC();
+#endif
+
+private:
+ CWeapon_SLAM( const CWeapon_SLAM & );
+};
+
+
+#endif //WEAPONSLAM_H
diff --git a/mp/src/game/shared/hl2mp/weapon_smg1.cpp b/mp/src/game/shared/hl2mp/weapon_smg1.cpp
index bc1b3c66..bd6e1d42 100644
--- a/mp/src/game/shared/hl2mp/weapon_smg1.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_smg1.cpp
@@ -1,264 +1,264 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "in_buttons.h"
-
-#ifdef CLIENT_DLL
- #include "c_hl2mp_player.h"
-#else
- #include "grenade_ar2.h"
- #include "hl2mp_player.h"
- #include "basegrenade_shared.h"
-#endif
-
-#include "weapon_hl2mpbase.h"
-#include "weapon_hl2mpbase_machinegun.h"
-
-#ifdef CLIENT_DLL
-#define CWeaponSMG1 C_WeaponSMG1
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-#define SMG1_GRENADE_DAMAGE 100.0f
-#define SMG1_GRENADE_RADIUS 250.0f
-
-class CWeaponSMG1 : public CHL2MPMachineGun
-{
-public:
- DECLARE_CLASS( CWeaponSMG1, CHL2MPMachineGun );
-
- CWeaponSMG1();
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
-
- void Precache( void );
- void AddViewKick( void );
- void SecondaryAttack( void );
-
- int GetMinBurst() { return 2; }
- int GetMaxBurst() { return 5; }
-
- virtual void Equip( CBaseCombatCharacter *pOwner );
- bool Reload( void );
-
- float GetFireRate( void ) { return 0.075f; } // 13.3hz
- Activity GetPrimaryAttackActivity( void );
-
- virtual const Vector& GetBulletSpread( void )
- {
- static const Vector cone = VECTOR_CONE_5DEGREES;
- return cone;
- }
-
- const WeaponProficiencyInfo_t *GetProficiencyValues();
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-
-protected:
-
- Vector m_vecTossVelocity;
- float m_flNextGrenadeCheck;
-
-private:
- CWeaponSMG1( const CWeaponSMG1 & );
-};
-
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSMG1, DT_WeaponSMG1 )
-
-BEGIN_NETWORK_TABLE( CWeaponSMG1, DT_WeaponSMG1 )
-END_NETWORK_TABLE()
-
-BEGIN_PREDICTION_DATA( CWeaponSMG1 )
-END_PREDICTION_DATA()
-
-LINK_ENTITY_TO_CLASS( weapon_smg1, CWeaponSMG1 );
-PRECACHE_WEAPON_REGISTER(weapon_smg1);
-
-#ifndef CLIENT_DLL
-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 },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponSMG1);
-#endif
-
-//=========================================================
-CWeaponSMG1::CWeaponSMG1( )
-{
- m_fMinRange1 = 0;// No minimum range.
- m_fMaxRange1 = 1400;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponSMG1::Precache( void )
-{
-#ifndef CLIENT_DLL
- UTIL_PrecacheOther("grenade_ar2");
-#endif
-
- BaseClass::Precache();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Give this weapon longer range when wielded by an ally NPC.
-//-----------------------------------------------------------------------------
-void CWeaponSMG1::Equip( CBaseCombatCharacter *pOwner )
-{
- m_fMaxRange1 = 1400;
-
- BaseClass::Equip( pOwner );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Activity
-//-----------------------------------------------------------------------------
-Activity CWeaponSMG1::GetPrimaryAttackActivity( void )
-{
- if ( m_nShotsFired < 2 )
- return ACT_VM_PRIMARYATTACK;
-
- if ( m_nShotsFired < 3 )
- return ACT_VM_RECOIL1;
-
- if ( m_nShotsFired < 4 )
- return ACT_VM_RECOIL2;
-
- return ACT_VM_RECOIL3;
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-bool CWeaponSMG1::Reload( void )
-{
- bool fRet;
- float fCacheTime = m_flNextSecondaryAttack;
-
- fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
- if ( fRet )
- {
- // Undo whatever the reload process has done to our secondary
- // attack timer. We allow you to interrupt reloading to fire
- // a grenade.
- m_flNextSecondaryAttack = GetOwner()->m_flNextAttack = fCacheTime;
-
- WeaponSound( RELOAD );
- }
-
- return fRet;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponSMG1::AddViewKick( void )
-{
- #define EASY_DAMPEN 0.5f
- #define MAX_VERTICAL_KICK 1.0f //Degrees
- #define SLIDE_LIMIT 2.0f //Seconds
-
- //Get the view kick
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if ( pPlayer == NULL )
- return;
-
- DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK, m_fFireDuration, SLIDE_LIMIT );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponSMG1::SecondaryAttack( void )
-{
- // Only the player fires this way so we can cast
- CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
-
- if ( pPlayer == NULL )
- return;
-
- //Must have ammo
- if ( ( pPlayer->GetAmmoCount( m_iSecondaryAmmoType ) <= 0 ) || ( pPlayer->GetWaterLevel() == 3 ) )
- {
- SendWeaponAnim( ACT_VM_DRYFIRE );
- BaseClass::WeaponSound( EMPTY );
- m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
- return;
- }
-
- if( m_bInReload )
- m_bInReload = false;
-
- // MUST call sound before removing a round from the clip of a CMachineGun
- BaseClass::WeaponSound( WPN_DOUBLE );
-
- Vector vecSrc = pPlayer->Weapon_ShootPosition();
- Vector vecThrow;
- // Don't autoaim on grenade tosses
- AngleVectors( pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), &vecThrow );
- VectorScale( vecThrow, 1000.0f, vecThrow );
-
-#ifndef CLIENT_DLL
- //Create the grenade
- CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecSrc, vec3_angle, pPlayer );
- pGrenade->SetAbsVelocity( vecThrow );
-
- pGrenade->SetLocalAngularVelocity( RandomAngle( -400, 400 ) );
- pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
- pGrenade->SetThrower( GetOwner() );
- pGrenade->SetDamage( SMG1_GRENADE_DAMAGE );
- pGrenade->SetDamageRadius( SMG1_GRENADE_RADIUS );
-#endif
-
- SendWeaponAnim( ACT_VM_SECONDARYATTACK );
-
- // player "shoot" animation
- pPlayer->SetAnimation( PLAYER_ATTACK1 );
-
- // Decrease ammo
- pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType );
-
- // Can shoot again immediately
- m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
-
- // Can blow up after a short delay (so have time to release mouse button)
- m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
-}
-
-//-----------------------------------------------------------------------------
-const WeaponProficiencyInfo_t *CWeaponSMG1::GetProficiencyValues()
-{
- static WeaponProficiencyInfo_t proficiencyTable[] =
- {
- { 7.0, 0.75 },
- { 5.00, 0.75 },
- { 10.0/3.0, 0.75 },
- { 5.0/3.0, 0.75 },
- { 1.00, 1.0 },
- };
-
- COMPILE_TIME_ASSERT( ARRAYSIZE(proficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1);
-
- return proficiencyTable;
-}
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "in_buttons.h"
+
+#ifdef CLIENT_DLL
+ #include "c_hl2mp_player.h"
+#else
+ #include "grenade_ar2.h"
+ #include "hl2mp_player.h"
+ #include "basegrenade_shared.h"
+#endif
+
+#include "weapon_hl2mpbase.h"
+#include "weapon_hl2mpbase_machinegun.h"
+
+#ifdef CLIENT_DLL
+#define CWeaponSMG1 C_WeaponSMG1
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define SMG1_GRENADE_DAMAGE 100.0f
+#define SMG1_GRENADE_RADIUS 250.0f
+
+class CWeaponSMG1 : public CHL2MPMachineGun
+{
+public:
+ DECLARE_CLASS( CWeaponSMG1, CHL2MPMachineGun );
+
+ CWeaponSMG1();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+
+ void Precache( void );
+ void AddViewKick( void );
+ void SecondaryAttack( void );
+
+ int GetMinBurst() { return 2; }
+ int GetMaxBurst() { return 5; }
+
+ virtual void Equip( CBaseCombatCharacter *pOwner );
+ bool Reload( void );
+
+ float GetFireRate( void ) { return 0.075f; } // 13.3hz
+ Activity GetPrimaryAttackActivity( void );
+
+ virtual const Vector& GetBulletSpread( void )
+ {
+ static const Vector cone = VECTOR_CONE_5DEGREES;
+ return cone;
+ }
+
+ const WeaponProficiencyInfo_t *GetProficiencyValues();
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+
+protected:
+
+ Vector m_vecTossVelocity;
+ float m_flNextGrenadeCheck;
+
+private:
+ CWeaponSMG1( const CWeaponSMG1 & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSMG1, DT_WeaponSMG1 )
+
+BEGIN_NETWORK_TABLE( CWeaponSMG1, DT_WeaponSMG1 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponSMG1 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_smg1, CWeaponSMG1 );
+PRECACHE_WEAPON_REGISTER(weapon_smg1);
+
+#ifndef CLIENT_DLL
+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 },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponSMG1);
+#endif
+
+//=========================================================
+CWeaponSMG1::CWeaponSMG1( )
+{
+ m_fMinRange1 = 0;// No minimum range.
+ m_fMaxRange1 = 1400;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponSMG1::Precache( void )
+{
+#ifndef CLIENT_DLL
+ UTIL_PrecacheOther("grenade_ar2");
+#endif
+
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Give this weapon longer range when wielded by an ally NPC.
+//-----------------------------------------------------------------------------
+void CWeaponSMG1::Equip( CBaseCombatCharacter *pOwner )
+{
+ m_fMaxRange1 = 1400;
+
+ BaseClass::Equip( pOwner );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Activity
+//-----------------------------------------------------------------------------
+Activity CWeaponSMG1::GetPrimaryAttackActivity( void )
+{
+ if ( m_nShotsFired < 2 )
+ return ACT_VM_PRIMARYATTACK;
+
+ if ( m_nShotsFired < 3 )
+ return ACT_VM_RECOIL1;
+
+ if ( m_nShotsFired < 4 )
+ return ACT_VM_RECOIL2;
+
+ return ACT_VM_RECOIL3;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool CWeaponSMG1::Reload( void )
+{
+ bool fRet;
+ float fCacheTime = m_flNextSecondaryAttack;
+
+ fRet = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
+ if ( fRet )
+ {
+ // Undo whatever the reload process has done to our secondary
+ // attack timer. We allow you to interrupt reloading to fire
+ // a grenade.
+ m_flNextSecondaryAttack = GetOwner()->m_flNextAttack = fCacheTime;
+
+ WeaponSound( RELOAD );
+ }
+
+ return fRet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponSMG1::AddViewKick( void )
+{
+ #define EASY_DAMPEN 0.5f
+ #define MAX_VERTICAL_KICK 1.0f //Degrees
+ #define SLIDE_LIMIT 2.0f //Seconds
+
+ //Get the view kick
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if ( pPlayer == NULL )
+ return;
+
+ DoMachineGunKick( pPlayer, EASY_DAMPEN, MAX_VERTICAL_KICK, m_fFireDuration, SLIDE_LIMIT );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponSMG1::SecondaryAttack( void )
+{
+ // Only the player fires this way so we can cast
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+
+ if ( pPlayer == NULL )
+ return;
+
+ //Must have ammo
+ if ( ( pPlayer->GetAmmoCount( m_iSecondaryAmmoType ) <= 0 ) || ( pPlayer->GetWaterLevel() == 3 ) )
+ {
+ SendWeaponAnim( ACT_VM_DRYFIRE );
+ BaseClass::WeaponSound( EMPTY );
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;
+ return;
+ }
+
+ if( m_bInReload )
+ m_bInReload = false;
+
+ // MUST call sound before removing a round from the clip of a CMachineGun
+ BaseClass::WeaponSound( WPN_DOUBLE );
+
+ Vector vecSrc = pPlayer->Weapon_ShootPosition();
+ Vector vecThrow;
+ // Don't autoaim on grenade tosses
+ AngleVectors( pPlayer->EyeAngles() + pPlayer->GetPunchAngle(), &vecThrow );
+ VectorScale( vecThrow, 1000.0f, vecThrow );
+
+#ifndef CLIENT_DLL
+ //Create the grenade
+ CGrenadeAR2 *pGrenade = (CGrenadeAR2*)Create( "grenade_ar2", vecSrc, vec3_angle, pPlayer );
+ pGrenade->SetAbsVelocity( vecThrow );
+
+ pGrenade->SetLocalAngularVelocity( RandomAngle( -400, 400 ) );
+ pGrenade->SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
+ pGrenade->SetThrower( GetOwner() );
+ pGrenade->SetDamage( SMG1_GRENADE_DAMAGE );
+ pGrenade->SetDamageRadius( SMG1_GRENADE_RADIUS );
+#endif
+
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ // Decrease ammo
+ pPlayer->RemoveAmmo( 1, m_iSecondaryAmmoType );
+
+ // Can shoot again immediately
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5f;
+
+ // Can blow up after a short delay (so have time to release mouse button)
+ m_flNextSecondaryAttack = gpGlobals->curtime + 1.0f;
+}
+
+//-----------------------------------------------------------------------------
+const WeaponProficiencyInfo_t *CWeaponSMG1::GetProficiencyValues()
+{
+ static WeaponProficiencyInfo_t proficiencyTable[] =
+ {
+ { 7.0, 0.75 },
+ { 5.00, 0.75 },
+ { 10.0/3.0, 0.75 },
+ { 5.0/3.0, 0.75 },
+ { 1.00, 1.0 },
+ };
+
+ COMPILE_TIME_ASSERT( ARRAYSIZE(proficiencyTable) == WEAPON_PROFICIENCY_PERFECT + 1);
+
+ return proficiencyTable;
+}
diff --git a/mp/src/game/shared/hl2mp/weapon_stunstick.cpp b/mp/src/game/shared/hl2mp/weapon_stunstick.cpp
index 0baec498..9f706983 100644
--- a/mp/src/game/shared/hl2mp/weapon_stunstick.cpp
+++ b/mp/src/game/shared/hl2mp/weapon_stunstick.cpp
@@ -1,902 +1,902 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose: Stun Stick- beating stick with a zappy end
-//
-// $NoKeywords: $
-//=============================================================================//
-
-#include "cbase.h"
-#include "npcevent.h"
-#include "weapon_hl2mpbasebasebludgeon.h"
-#include "IEffects.h"
-#include "debugoverlay_shared.h"
-
-#ifndef CLIENT_DLL
- #include "npc_metropolice.h"
- #include "te_effect_dispatch.h"
-#endif
-
-#ifdef CLIENT_DLL
-
- #include "iviewrender_beams.h"
- #include "beam_shared.h"
- #include "materialsystem/imaterial.h"
- #include "model_types.h"
- #include "c_te_effect_dispatch.h"
- #include "fx_quad.h"
- #include "fx.h"
-
- extern void DrawHalo( IMaterial* pMaterial, const Vector &source, float scale, float const *color, float flHDRColorScale );
- extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
-
-#endif
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include "tier0/memdbgon.h"
-
-extern ConVar metropolice_move_and_melee;
-
-#define STUNSTICK_RANGE 75.0f
-#define STUNSTICK_REFIRE 0.8f
-#define STUNSTICK_BEAM_MATERIAL "sprites/lgtning.vmt"
-#define STUNSTICK_GLOW_MATERIAL "sprites/light_glow02_add"
-#define STUNSTICK_GLOW_MATERIAL2 "effects/blueflare1"
-#define STUNSTICK_GLOW_MATERIAL_NOZ "sprites/light_glow02_add_noz"
-
-#ifdef CLIENT_DLL
-#define CWeaponStunStick C_WeaponStunStick
-#endif
-
-class CWeaponStunStick : public CBaseHL2MPBludgeonWeapon
-{
- DECLARE_CLASS( CWeaponStunStick, CBaseHL2MPBludgeonWeapon );
-
-public:
-
- CWeaponStunStick();
-
- DECLARE_NETWORKCLASS();
- DECLARE_PREDICTABLE();
-
-#ifndef CLIENT_DLL
- DECLARE_ACTTABLE();
-#endif
-
-#ifdef CLIENT_DLL
- virtual int DrawModel( int flags );
- virtual void ClientThink( void );
- virtual void OnDataChanged( DataUpdateType_t updateType );
- virtual RenderGroup_t GetRenderGroup( void );
- virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
-
-#endif
-
- virtual void Precache();
-
- void Spawn();
-
- float GetRange( void ) { return STUNSTICK_RANGE; }
- float GetFireRate( void ) { return STUNSTICK_REFIRE; }
-
-
- bool Deploy( void );
- bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
-
- void Drop( const Vector &vecVelocity );
- void ImpactEffect( trace_t &traceHit );
- void SecondaryAttack( void ) {}
- void SetStunState( bool state );
- bool GetStunState( void );
-
-#ifndef CLIENT_DLL
- void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
- int WeaponMeleeAttack1Condition( float flDot, float flDist );
-#endif
-
- float GetDamageForActivity( Activity hitActivity );
-
- CWeaponStunStick( const CWeaponStunStick & );
-
-private:
-
-#ifdef CLIENT_DLL
-
- #define NUM_BEAM_ATTACHMENTS 9
-
- struct stunstickBeamInfo_t
- {
- int IDs[2]; // 0 - top, 1 - bottom
- };
-
- stunstickBeamInfo_t m_BeamAttachments[NUM_BEAM_ATTACHMENTS]; // Lookup for arc attachment points on the head of the stick
- int m_BeamCenterAttachment; // "Core" of the effect (center of the head)
-
- void SetupAttachmentPoints( void );
- void DrawFirstPersonEffects( void );
- void DrawThirdPersonEffects( void );
- void DrawEffects( void );
- bool InSwing( void );
-
- bool m_bSwungLastFrame;
-
- #define FADE_DURATION 0.25f
-
- float m_flFadeTime;
-
-#endif
-
- CNetworkVar( bool, m_bActive );
-};
-
-//-----------------------------------------------------------------------------
-// CWeaponStunStick
-//-----------------------------------------------------------------------------
-IMPLEMENT_NETWORKCLASS_ALIASED( WeaponStunStick, DT_WeaponStunStick )
-
-BEGIN_NETWORK_TABLE( CWeaponStunStick, DT_WeaponStunStick )
-#ifdef CLIENT_DLL
- RecvPropInt( RECVINFO( m_bActive ) ),
-#else
- SendPropInt( SENDINFO( m_bActive ), 1, SPROP_UNSIGNED ),
-#endif
-
-END_NETWORK_TABLE()
-
-BEGIN_PREDICTION_DATA( CWeaponStunStick )
-END_PREDICTION_DATA()
-
-LINK_ENTITY_TO_CLASS( weapon_stunstick, CWeaponStunStick );
-PRECACHE_WEAPON_REGISTER( weapon_stunstick );
-
-
-#ifndef CLIENT_DLL
-
-acttable_t CWeaponStunStick::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 },
-};
-
-IMPLEMENT_ACTTABLE(CWeaponStunStick);
-
-#endif
-
-
-//-----------------------------------------------------------------------------
-// Constructor
-//-----------------------------------------------------------------------------
-CWeaponStunStick::CWeaponStunStick( void )
-{
- // HACK: Don't call SetStunState because this tried to Emit a sound before
- // any players are connected which is a bug
- m_bActive = false;
-
-#ifdef CLIENT_DLL
- m_bSwungLastFrame = false;
- m_flFadeTime = FADE_DURATION; // Start off past the fade point
-#endif
-}
-
-//-----------------------------------------------------------------------------
-//-----------------------------------------------------------------------------
-void CWeaponStunStick::Spawn()
-{
- Precache();
-
- BaseClass::Spawn();
- AddSolidFlags( FSOLID_NOT_STANDABLE );
-}
-
-void CWeaponStunStick::Precache()
-{
- BaseClass::Precache();
-
- PrecacheScriptSound( "Weapon_StunStick.Activate" );
- PrecacheScriptSound( "Weapon_StunStick.Deactivate" );
-
- PrecacheModel( STUNSTICK_BEAM_MATERIAL );
- PrecacheModel( "sprites/light_glow02_add.vmt" );
- PrecacheModel( "effects/blueflare1.vmt" );
- PrecacheModel( "sprites/light_glow02_add_noz.vmt" );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the damage amount for the animation we're doing
-// Input : hitActivity - currently played activity
-// Output : Damage amount
-//-----------------------------------------------------------------------------
-float CWeaponStunStick::GetDamageForActivity( Activity hitActivity )
-{
- return 40.0f;
-}
-
-//-----------------------------------------------------------------------------
-// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
-//-----------------------------------------------------------------------------
-extern ConVar sk_crowbar_lead_time;
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-void CWeaponStunStick::ImpactEffect( trace_t &traceHit )
-{
-
-//#ifndef CLIENT_DLL
-
- CEffectData data;
-
- data.m_vNormal = traceHit.plane.normal;
- data.m_vOrigin = traceHit.endpos + ( data.m_vNormal * 4.0f );
-
- DispatchEffect( "StunstickImpact", data );
-
-//#endif
-
- //FIXME: need new decals
- UTIL_ImpactTrace( &traceHit, DMG_CLUB );
-}
-
-#ifndef CLIENT_DLL
-
-
-int CWeaponStunStick::WeaponMeleeAttack1Condition( float flDot, float flDist )
-{
- // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
- CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
- CBaseEntity *pEnemy = pNPC->GetEnemy();
- if (!pEnemy)
- return COND_NONE;
-
- Vector vecVelocity;
- AngularImpulse angVelocity;
- pEnemy->GetVelocity( &vecVelocity, &angVelocity );
-
- // Project where the enemy will be in a little while, add some randomness so he doesn't always hit
- float dt = sk_crowbar_lead_time.GetFloat();
- dt += random->RandomFloat( -0.3f, 0.2f );
- if ( dt < 0.0f )
- dt = 0.0f;
-
- Vector vecExtrapolatedPos;
- VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos );
-
- Vector vecDelta;
- VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta );
-
- if ( fabs( vecDelta.z ) > 70 )
- {
- return COND_TOO_FAR_TO_ATTACK;
- }
-
- Vector vecForward = pNPC->BodyDirection2D( );
- vecDelta.z = 0.0f;
- float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() );
- if ((flDot < 0.7) && (flExtrapolatedDot < 0.7))
- {
- return COND_NOT_FACING_ATTACK;
- }
-
- float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() );
-
- if( pEnemy->IsPlayer() )
- {
- //Vector vecDir = pEnemy->GetSmoothedVelocity();
- //float flSpeed = VectorNormalize( vecDir );
-
- // If player will be in front of me in one-half second, clock his arse.
- Vector vecProjectEnemy = pEnemy->GetAbsOrigin() + (pEnemy->GetAbsVelocity() * 0.35);
- Vector vecProjectMe = GetAbsOrigin();
-
- if( (vecProjectMe - vecProjectEnemy).Length2D() <= 48.0f )
- {
- return COND_CAN_MELEE_ATTACK1;
- }
- }
-/*
- if( metropolice_move_and_melee.GetBool() )
- {
- if( pNPC->IsMoving() )
- {
- flTargetDist *= 1.5f;
- }
- }
-*/
- float flTargetDist = 48.0f;
- if ((flDist > flTargetDist) && (flExtrapolatedDist > flTargetDist))
- {
- return COND_TOO_FAR_TO_ATTACK;
- }
-
- return COND_CAN_MELEE_ATTACK1;
-}
-
-
-void CWeaponStunStick::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
-{
- switch( pEvent->event )
- {
- case EVENT_WEAPON_MELEE_HIT:
- {
- // 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(), 32, vecDirection, vecEnd );
- // Stretch the swing box down to catch low level physics objects
- CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd,
- Vector(-16,-16,-40), Vector(16,16,16), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.5f, false );
-
- // did I hit someone?
- if ( pHurt )
- {
- // play sound
- WeaponSound( MELEE_HIT );
-
- CBasePlayer *pPlayer = ToBasePlayer( pHurt );
-
- bool bFlashed = false;
-
- // Punch angles
- if ( pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE) )
- {
- float yawKick = random->RandomFloat( -48, -24 );
-
- //Kick the player angles
- pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) );
-
- Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
-
- // If the player's on my head, don't knock him up
- if ( pPlayer->GetGroundEntity() == pOperator )
- {
- dir = vecDirection;
- dir.z = 0;
- }
-
- VectorNormalize(dir);
-
- dir *= 500.0f;
-
- //If not on ground, then don't make them fly!
- if ( !(pPlayer->GetFlags() & FL_ONGROUND ) )
- dir.z = 0.0f;
-
- //Push the target back
- pHurt->ApplyAbsVelocityImpulse( dir );
-
- if ( !bFlashed )
- {
- color32 red = {128,0,0,128};
- UTIL_ScreenFade( pPlayer, red, 0.5f, 0.1f, FFADE_IN );
- }
-
- // Force the player to drop anyting they were holding
- pPlayer->ForceDropOfCarriedPhysObjects();
- }
-
- // do effect?
- }
- else
- {
- WeaponSound( MELEE_MISS );
- }
- }
- break;
- default:
- BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
- break;
- }
-}
-
-#endif
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets the state of the stun stick
-//-----------------------------------------------------------------------------
-void CWeaponStunStick::SetStunState( bool state )
-{
- m_bActive = state;
-
- if ( m_bActive )
- {
- //FIXME: START - Move to client-side
-
- Vector vecAttachment;
- QAngle vecAttachmentAngles;
-
- GetAttachment( 1, vecAttachment, vecAttachmentAngles );
- g_pEffects->Sparks( vecAttachment );
-
- //FIXME: END - Move to client-side
-
- EmitSound( "Weapon_StunStick.Activate" );
- }
- else
- {
- EmitSound( "Weapon_StunStick.Deactivate" );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponStunStick::Deploy( void )
-{
- SetStunState( true );
-
- return BaseClass::Deploy();
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-//-----------------------------------------------------------------------------
-bool CWeaponStunStick::Holster( CBaseCombatWeapon *pSwitchingTo )
-{
- if ( BaseClass::Holster( pSwitchingTo ) == false )
- return false;
-
- SetStunState( false );
- SetWeaponVisible( false );
-
- return true;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Input : &vecVelocity -
-//-----------------------------------------------------------------------------
-void CWeaponStunStick::Drop( const Vector &vecVelocity )
-{
- SetStunState( false );
-
-#ifndef CLIENT_DLL
- UTIL_Remove( this );
-#endif
-
-}
-
-//-----------------------------------------------------------------------------
-// Purpose:
-// Output : Returns true on success, false on failure.
-//-----------------------------------------------------------------------------
-bool CWeaponStunStick::GetStunState( void )
-{
- return m_bActive;
-}
-
-#ifdef CLIENT_DLL
-
-//-----------------------------------------------------------------------------
-// Purpose: Get the attachment point on a viewmodel that a base weapon is using
-//-----------------------------------------------------------------------------
-bool UTIL_GetWeaponAttachment( C_BaseCombatWeapon *pWeapon, int attachmentID, Vector &absOrigin, QAngle &absAngles )
-{
- // This is already correct in third-person
- if ( pWeapon && pWeapon->ShouldDrawUsingViewModel() == false )
- {
- return pWeapon->GetAttachment( attachmentID, absOrigin, absAngles );
- }
-
- // Otherwise we need to translate the attachment to the viewmodel's version and reformat it
- CBasePlayer *pOwner = ToBasePlayer( pWeapon->GetOwner() );
-
- if ( pOwner != NULL )
- {
- int ret = pOwner->GetViewModel()->GetAttachment( attachmentID, absOrigin, absAngles );
- FormatViewModelAttachment( absOrigin, true );
-
- return ret;
- }
-
- // Wasn't found
- return false;
-}
-
-#define BEAM_ATTACH_CORE_NAME "sparkrear"
-
-//-----------------------------------------------------------------------------
-// Purpose: Sets up the attachment point lookup for the model
-//-----------------------------------------------------------------------------
-void C_WeaponStunStick::SetupAttachmentPoints( void )
-{
- // Setup points for both types of views
- if ( ShouldDrawUsingViewModel() )
- {
- const char *szBeamAttachNamesTop[NUM_BEAM_ATTACHMENTS] =
- {
- "spark1a","spark2a","spark3a","spark4a",
- "spark5a","spark6a","spark7a","spark8a",
- "spark9a",
- };
-
- const char *szBeamAttachNamesBottom[NUM_BEAM_ATTACHMENTS] =
- {
- "spark1b","spark2b","spark3b","spark4b",
- "spark5b","spark6b","spark7b","spark8b",
- "spark9b",
- };
-
- // Lookup and store all connections
- for ( int i = 0; i < NUM_BEAM_ATTACHMENTS; i++ )
- {
- m_BeamAttachments[i].IDs[0] = LookupAttachment( szBeamAttachNamesTop[i] );
- m_BeamAttachments[i].IDs[1] = LookupAttachment( szBeamAttachNamesBottom[i] );
- }
-
- // Setup the center beam point
- m_BeamCenterAttachment = LookupAttachment( BEAM_ATTACH_CORE_NAME );
- }
- else
- {
- // Setup the center beam point
- m_BeamCenterAttachment = 1;
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draws the stunstick model (with extra effects)
-//-----------------------------------------------------------------------------
-int C_WeaponStunStick::DrawModel( int flags )
-{
- if ( ShouldDraw() == false )
- return 0;
-
- // Only render these on the transparent pass
- if ( flags & STUDIO_TRANSPARENCY )
- {
- DrawEffects();
- return 1;
- }
-
- return BaseClass::DrawModel( flags );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Randomly adds extra effects
-//-----------------------------------------------------------------------------
-void C_WeaponStunStick::ClientThink( void )
-{
- if ( InSwing() == false )
- {
- if ( m_bSwungLastFrame )
- {
- // Start fading
- m_flFadeTime = gpGlobals->curtime;
- m_bSwungLastFrame = false;
- }
-
- return;
- }
-
- // Remember if we were swinging last frame
- m_bSwungLastFrame = InSwing();
-
- if ( IsEffectActive( EF_NODRAW ) )
- return;
-
- if ( ShouldDrawUsingViewModel() )
- {
- // Update our effects
- if ( gpGlobals->frametime != 0.0f && ( random->RandomInt( 0, 3 ) == 0 ) )
- {
- Vector vecOrigin;
- QAngle vecAngles;
-
- // Inner beams
- BeamInfo_t beamInfo;
-
- int attachment = random->RandomInt( 0, 15 );
-
- UTIL_GetWeaponAttachment( this, attachment, vecOrigin, vecAngles );
- ::FormatViewModelAttachment( vecOrigin, false );
-
- CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
- CBaseEntity *pBeamEnt = pOwner->GetViewModel();
-
- beamInfo.m_vecStart = vec3_origin;
- beamInfo.m_pStartEnt= pBeamEnt;
- beamInfo.m_nStartAttachment = attachment;
-
- beamInfo.m_pEndEnt = NULL;
- beamInfo.m_nEndAttachment = -1;
- beamInfo.m_vecEnd = vecOrigin + RandomVector( -8, 8 );
-
- beamInfo.m_pszModelName = STUNSTICK_BEAM_MATERIAL;
- beamInfo.m_flHaloScale = 0.0f;
- beamInfo.m_flLife = 0.05f;
- beamInfo.m_flWidth = random->RandomFloat( 1.0f, 2.0f );
- beamInfo.m_flEndWidth = 0;
- beamInfo.m_flFadeLength = 0.0f;
- beamInfo.m_flAmplitude = random->RandomFloat( 16, 32 );
- beamInfo.m_flBrightness = 255.0;
- beamInfo.m_flSpeed = 0.0;
- beamInfo.m_nStartFrame = 0.0;
- beamInfo.m_flFrameRate = 1.0f;
- beamInfo.m_flRed = 255.0f;;
- beamInfo.m_flGreen = 255.0f;
- beamInfo.m_flBlue = 255.0f;
- beamInfo.m_nSegments = 16;
- beamInfo.m_bRenderable = true;
- beamInfo.m_nFlags = 0;
-
- beams->CreateBeamEntPoint( beamInfo );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Starts the client-side version thinking
-//-----------------------------------------------------------------------------
-void C_WeaponStunStick::OnDataChanged( DataUpdateType_t updateType )
-{
- BaseClass::OnDataChanged( updateType );
- if ( updateType == DATA_UPDATE_CREATED )
- {
- SetNextClientThink( CLIENT_THINK_ALWAYS );
- SetupAttachmentPoints();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Tells us we're always a translucent entity
-//-----------------------------------------------------------------------------
-RenderGroup_t C_WeaponStunStick::GetRenderGroup( void )
-{
- return RENDER_GROUP_TWOPASS;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Tells us we're always a translucent entity
-//-----------------------------------------------------------------------------
-bool C_WeaponStunStick::InSwing( void )
-{
- int activity = GetActivity();
-
- // FIXME: This is needed until the actual animation works
- if ( ShouldDrawUsingViewModel() == false )
- return true;
-
- // These are the swing activities this weapon can play
- if ( activity == GetPrimaryAttackActivity() ||
- activity == GetSecondaryAttackActivity() ||
- activity == ACT_VM_MISSCENTER ||
- activity == ACT_VM_MISSCENTER2 )
- return true;
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw our special effects
-//-----------------------------------------------------------------------------
-void C_WeaponStunStick::DrawThirdPersonEffects( void )
-{
- Vector vecOrigin;
- QAngle vecAngles;
- float color[3];
- float scale;
-
- CMatRenderContextPtr pRenderContext( materials );
- IMaterial *pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL, NULL, false );
- pRenderContext->Bind( pMaterial );
-
- // Get bright when swung
- if ( InSwing() )
- {
- color[0] = color[1] = color[2] = 0.4f;
- scale = 22.0f;
- }
- else
- {
- color[0] = color[1] = color[2] = 0.1f;
- scale = 20.0f;
- }
-
- // Draw an all encompassing glow around the entire head
- UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles );
- DrawHalo( pMaterial, vecOrigin, scale, color );
-
- if ( InSwing() )
- {
- pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL2, NULL, false );
- pRenderContext->Bind( pMaterial );
-
- color[0] = color[1] = color[2] = random->RandomFloat( 0.6f, 0.8f );
- scale = random->RandomFloat( 4.0f, 6.0f );
-
- // Draw an all encompassing glow around the entire head
- UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles );
- DrawHalo( pMaterial, vecOrigin, scale, color );
-
- // Update our effects
- if ( gpGlobals->frametime != 0.0f && ( random->RandomInt( 0, 5 ) == 0 ) )
- {
- Vector vecOrigin;
- QAngle vecAngles;
-
- GetAttachment( 1, vecOrigin, vecAngles );
-
- Vector vForward;
- AngleVectors( vecAngles, &vForward );
-
- Vector vEnd = vecOrigin - vForward * 1.0f;
-
- // Inner beams
- BeamInfo_t beamInfo;
-
- beamInfo.m_vecStart = vEnd;
- Vector offset = RandomVector( -12, 8 );
-
- offset += Vector(4,4,4);
- beamInfo.m_vecEnd = vecOrigin + offset;
-
- beamInfo.m_pStartEnt= cl_entitylist->GetEnt( BEAMENT_ENTITY( entindex() ) );
- beamInfo.m_pEndEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( entindex() ) );
- beamInfo.m_nStartAttachment = 1;
- beamInfo.m_nEndAttachment = -1;
-
- beamInfo.m_nType = TE_BEAMTESLA;
- beamInfo.m_pszModelName = STUNSTICK_BEAM_MATERIAL;
- beamInfo.m_flHaloScale = 0.0f;
- beamInfo.m_flLife = 0.01f;
- beamInfo.m_flWidth = random->RandomFloat( 1.0f, 3.0f );
- beamInfo.m_flEndWidth = 0;
- beamInfo.m_flFadeLength = 0.0f;
- beamInfo.m_flAmplitude = random->RandomFloat( 1, 2 );
- beamInfo.m_flBrightness = 255.0;
- beamInfo.m_flSpeed = 0.0;
- beamInfo.m_nStartFrame = 0.0;
- beamInfo.m_flFrameRate = 1.0f;
- beamInfo.m_flRed = 255.0f;;
- beamInfo.m_flGreen = 255.0f;
- beamInfo.m_flBlue = 255.0f;
- beamInfo.m_nSegments = 16;
- beamInfo.m_bRenderable = true;
- beamInfo.m_nFlags = FBEAM_SHADEOUT;
-
- beams->CreateBeamPoints( beamInfo );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw our special effects
-//-----------------------------------------------------------------------------
-void C_WeaponStunStick::DrawFirstPersonEffects( void )
-{
- Vector vecOrigin;
- QAngle vecAngles;
- float color[3];
- float scale;
-
- CMatRenderContextPtr pRenderContext( materials );
- IMaterial *pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL_NOZ, NULL, false );
- // FIXME: Needs to work with new IMaterial system!
- pRenderContext->Bind( pMaterial );
-
- // Find where we are in the fade
- float fadeAmount = RemapValClamped( gpGlobals->curtime, m_flFadeTime, m_flFadeTime + FADE_DURATION, 1.0f, 0.1f );
-
- // Get bright when swung
- if ( InSwing() )
- {
- color[0] = color[1] = color[2] = 0.4f;
- scale = 22.0f;
- }
- else
- {
- color[0] = color[1] = color[2] = 0.4f * fadeAmount;
- scale = 20.0f;
- }
-
- if ( color[0] > 0.0f )
- {
- // Draw an all encompassing glow around the entire head
- UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles );
- DrawHalo( pMaterial, vecOrigin, scale, color );
- }
-
- // Draw bright points at each attachment location
- for ( int i = 0; i < (NUM_BEAM_ATTACHMENTS*2)+1; i++ )
- {
- if ( InSwing() )
- {
- color[0] = color[1] = color[2] = random->RandomFloat( 0.05f, 0.5f );
- scale = random->RandomFloat( 4.0f, 5.0f );
- }
- else
- {
- color[0] = color[1] = color[2] = random->RandomFloat( 0.05f, 0.5f ) * fadeAmount;
- scale = random->RandomFloat( 4.0f, 5.0f ) * fadeAmount;
- }
-
- if ( color[0] > 0.0f )
- {
- UTIL_GetWeaponAttachment( this, i, vecOrigin, vecAngles );
- DrawHalo( pMaterial, vecOrigin, scale, color );
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw our special effects
-//-----------------------------------------------------------------------------
-void C_WeaponStunStick::DrawEffects( void )
-{
- if ( ShouldDrawUsingViewModel() )
- {
- DrawFirstPersonEffects();
- }
- else
- {
- DrawThirdPersonEffects();
- }
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Viewmodel was drawn
-//-----------------------------------------------------------------------------
-void C_WeaponStunStick::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
-{
- // Don't bother when we're not deployed
- if ( IsWeaponVisible() )
- {
- // Do all our special effects
- DrawEffects();
- }
-
- BaseClass::ViewModelDrawn( pBaseViewModel );
-}
-
-//-----------------------------------------------------------------------------
-// Purpose: Draw a cheap glow quad at our impact point (with sparks)
-//-----------------------------------------------------------------------------
-void StunstickImpactCallback( const CEffectData &data )
-{
- float scale = random->RandomFloat( 16, 32 );
-
- FX_AddQuad( data.m_vOrigin,
- data.m_vNormal,
- scale,
- scale*2.0f,
- 1.0f,
- 1.0f,
- 0.0f,
- 0.0f,
- random->RandomInt( 0, 360 ),
- 0,
- Vector( 1.0f, 1.0f, 1.0f ),
- 0.1f,
- "sprites/light_glow02_add",
- 0 );
-
- FX_Sparks( data.m_vOrigin, 1, 2, data.m_vNormal, 6, 64, 256 );
-}
-
-DECLARE_CLIENT_EFFECT( "StunstickImpact", StunstickImpactCallback );
-
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Stun Stick- beating stick with a zappy end
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "npcevent.h"
+#include "weapon_hl2mpbasebasebludgeon.h"
+#include "IEffects.h"
+#include "debugoverlay_shared.h"
+
+#ifndef CLIENT_DLL
+ #include "npc_metropolice.h"
+ #include "te_effect_dispatch.h"
+#endif
+
+#ifdef CLIENT_DLL
+
+ #include "iviewrender_beams.h"
+ #include "beam_shared.h"
+ #include "materialsystem/imaterial.h"
+ #include "model_types.h"
+ #include "c_te_effect_dispatch.h"
+ #include "fx_quad.h"
+ #include "fx.h"
+
+ extern void DrawHalo( IMaterial* pMaterial, const Vector &source, float scale, float const *color, float flHDRColorScale );
+ extern void FormatViewModelAttachment( Vector &vOrigin, bool bInverse );
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern ConVar metropolice_move_and_melee;
+
+#define STUNSTICK_RANGE 75.0f
+#define STUNSTICK_REFIRE 0.8f
+#define STUNSTICK_BEAM_MATERIAL "sprites/lgtning.vmt"
+#define STUNSTICK_GLOW_MATERIAL "sprites/light_glow02_add"
+#define STUNSTICK_GLOW_MATERIAL2 "effects/blueflare1"
+#define STUNSTICK_GLOW_MATERIAL_NOZ "sprites/light_glow02_add_noz"
+
+#ifdef CLIENT_DLL
+#define CWeaponStunStick C_WeaponStunStick
+#endif
+
+class CWeaponStunStick : public CBaseHL2MPBludgeonWeapon
+{
+ DECLARE_CLASS( CWeaponStunStick, CBaseHL2MPBludgeonWeapon );
+
+public:
+
+ CWeaponStunStick();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+#ifndef CLIENT_DLL
+ DECLARE_ACTTABLE();
+#endif
+
+#ifdef CLIENT_DLL
+ virtual int DrawModel( int flags );
+ virtual void ClientThink( void );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual RenderGroup_t GetRenderGroup( void );
+ virtual void ViewModelDrawn( C_BaseViewModel *pBaseViewModel );
+
+#endif
+
+ virtual void Precache();
+
+ void Spawn();
+
+ float GetRange( void ) { return STUNSTICK_RANGE; }
+ float GetFireRate( void ) { return STUNSTICK_REFIRE; }
+
+
+ bool Deploy( void );
+ bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+
+ void Drop( const Vector &vecVelocity );
+ void ImpactEffect( trace_t &traceHit );
+ void SecondaryAttack( void ) {}
+ void SetStunState( bool state );
+ bool GetStunState( void );
+
+#ifndef CLIENT_DLL
+ void Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator );
+ int WeaponMeleeAttack1Condition( float flDot, float flDist );
+#endif
+
+ float GetDamageForActivity( Activity hitActivity );
+
+ CWeaponStunStick( const CWeaponStunStick & );
+
+private:
+
+#ifdef CLIENT_DLL
+
+ #define NUM_BEAM_ATTACHMENTS 9
+
+ struct stunstickBeamInfo_t
+ {
+ int IDs[2]; // 0 - top, 1 - bottom
+ };
+
+ stunstickBeamInfo_t m_BeamAttachments[NUM_BEAM_ATTACHMENTS]; // Lookup for arc attachment points on the head of the stick
+ int m_BeamCenterAttachment; // "Core" of the effect (center of the head)
+
+ void SetupAttachmentPoints( void );
+ void DrawFirstPersonEffects( void );
+ void DrawThirdPersonEffects( void );
+ void DrawEffects( void );
+ bool InSwing( void );
+
+ bool m_bSwungLastFrame;
+
+ #define FADE_DURATION 0.25f
+
+ float m_flFadeTime;
+
+#endif
+
+ CNetworkVar( bool, m_bActive );
+};
+
+//-----------------------------------------------------------------------------
+// CWeaponStunStick
+//-----------------------------------------------------------------------------
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponStunStick, DT_WeaponStunStick )
+
+BEGIN_NETWORK_TABLE( CWeaponStunStick, DT_WeaponStunStick )
+#ifdef CLIENT_DLL
+ RecvPropInt( RECVINFO( m_bActive ) ),
+#else
+ SendPropInt( SENDINFO( m_bActive ), 1, SPROP_UNSIGNED ),
+#endif
+
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponStunStick )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_stunstick, CWeaponStunStick );
+PRECACHE_WEAPON_REGISTER( weapon_stunstick );
+
+
+#ifndef CLIENT_DLL
+
+acttable_t CWeaponStunStick::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 },
+};
+
+IMPLEMENT_ACTTABLE(CWeaponStunStick);
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CWeaponStunStick::CWeaponStunStick( void )
+{
+ // HACK: Don't call SetStunState because this tried to Emit a sound before
+ // any players are connected which is a bug
+ m_bActive = false;
+
+#ifdef CLIENT_DLL
+ m_bSwungLastFrame = false;
+ m_flFadeTime = FADE_DURATION; // Start off past the fade point
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CWeaponStunStick::Spawn()
+{
+ Precache();
+
+ BaseClass::Spawn();
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+}
+
+void CWeaponStunStick::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "Weapon_StunStick.Activate" );
+ PrecacheScriptSound( "Weapon_StunStick.Deactivate" );
+
+ PrecacheModel( STUNSTICK_BEAM_MATERIAL );
+ PrecacheModel( "sprites/light_glow02_add.vmt" );
+ PrecacheModel( "effects/blueflare1.vmt" );
+ PrecacheModel( "sprites/light_glow02_add_noz.vmt" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the damage amount for the animation we're doing
+// Input : hitActivity - currently played activity
+// Output : Damage amount
+//-----------------------------------------------------------------------------
+float CWeaponStunStick::GetDamageForActivity( Activity hitActivity )
+{
+ return 40.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
+//-----------------------------------------------------------------------------
+extern ConVar sk_crowbar_lead_time;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponStunStick::ImpactEffect( trace_t &traceHit )
+{
+
+//#ifndef CLIENT_DLL
+
+ CEffectData data;
+
+ data.m_vNormal = traceHit.plane.normal;
+ data.m_vOrigin = traceHit.endpos + ( data.m_vNormal * 4.0f );
+
+ DispatchEffect( "StunstickImpact", data );
+
+//#endif
+
+ //FIXME: need new decals
+ UTIL_ImpactTrace( &traceHit, DMG_CLUB );
+}
+
+#ifndef CLIENT_DLL
+
+
+int CWeaponStunStick::WeaponMeleeAttack1Condition( float flDot, float flDist )
+{
+ // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
+ CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
+ CBaseEntity *pEnemy = pNPC->GetEnemy();
+ if (!pEnemy)
+ return COND_NONE;
+
+ Vector vecVelocity;
+ AngularImpulse angVelocity;
+ pEnemy->GetVelocity( &vecVelocity, &angVelocity );
+
+ // Project where the enemy will be in a little while, add some randomness so he doesn't always hit
+ float dt = sk_crowbar_lead_time.GetFloat();
+ dt += random->RandomFloat( -0.3f, 0.2f );
+ if ( dt < 0.0f )
+ dt = 0.0f;
+
+ Vector vecExtrapolatedPos;
+ VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos );
+
+ Vector vecDelta;
+ VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta );
+
+ if ( fabs( vecDelta.z ) > 70 )
+ {
+ return COND_TOO_FAR_TO_ATTACK;
+ }
+
+ Vector vecForward = pNPC->BodyDirection2D( );
+ vecDelta.z = 0.0f;
+ float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() );
+ if ((flDot < 0.7) && (flExtrapolatedDot < 0.7))
+ {
+ return COND_NOT_FACING_ATTACK;
+ }
+
+ float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() );
+
+ if( pEnemy->IsPlayer() )
+ {
+ //Vector vecDir = pEnemy->GetSmoothedVelocity();
+ //float flSpeed = VectorNormalize( vecDir );
+
+ // If player will be in front of me in one-half second, clock his arse.
+ Vector vecProjectEnemy = pEnemy->GetAbsOrigin() + (pEnemy->GetAbsVelocity() * 0.35);
+ Vector vecProjectMe = GetAbsOrigin();
+
+ if( (vecProjectMe - vecProjectEnemy).Length2D() <= 48.0f )
+ {
+ return COND_CAN_MELEE_ATTACK1;
+ }
+ }
+/*
+ if( metropolice_move_and_melee.GetBool() )
+ {
+ if( pNPC->IsMoving() )
+ {
+ flTargetDist *= 1.5f;
+ }
+ }
+*/
+ float flTargetDist = 48.0f;
+ if ((flDist > flTargetDist) && (flExtrapolatedDist > flTargetDist))
+ {
+ return COND_TOO_FAR_TO_ATTACK;
+ }
+
+ return COND_CAN_MELEE_ATTACK1;
+}
+
+
+void CWeaponStunStick::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
+{
+ switch( pEvent->event )
+ {
+ case EVENT_WEAPON_MELEE_HIT:
+ {
+ // 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(), 32, vecDirection, vecEnd );
+ // Stretch the swing box down to catch low level physics objects
+ CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd,
+ Vector(-16,-16,-40), Vector(16,16,16), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.5f, false );
+
+ // did I hit someone?
+ if ( pHurt )
+ {
+ // play sound
+ WeaponSound( MELEE_HIT );
+
+ CBasePlayer *pPlayer = ToBasePlayer( pHurt );
+
+ bool bFlashed = false;
+
+ // Punch angles
+ if ( pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE) )
+ {
+ float yawKick = random->RandomFloat( -48, -24 );
+
+ //Kick the player angles
+ pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) );
+
+ Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
+
+ // If the player's on my head, don't knock him up
+ if ( pPlayer->GetGroundEntity() == pOperator )
+ {
+ dir = vecDirection;
+ dir.z = 0;
+ }
+
+ VectorNormalize(dir);
+
+ dir *= 500.0f;
+
+ //If not on ground, then don't make them fly!
+ if ( !(pPlayer->GetFlags() & FL_ONGROUND ) )
+ dir.z = 0.0f;
+
+ //Push the target back
+ pHurt->ApplyAbsVelocityImpulse( dir );
+
+ if ( !bFlashed )
+ {
+ color32 red = {128,0,0,128};
+ UTIL_ScreenFade( pPlayer, red, 0.5f, 0.1f, FFADE_IN );
+ }
+
+ // Force the player to drop anyting they were holding
+ pPlayer->ForceDropOfCarriedPhysObjects();
+ }
+
+ // do effect?
+ }
+ else
+ {
+ WeaponSound( MELEE_MISS );
+ }
+ }
+ break;
+ default:
+ BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
+ break;
+ }
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets the state of the stun stick
+//-----------------------------------------------------------------------------
+void CWeaponStunStick::SetStunState( bool state )
+{
+ m_bActive = state;
+
+ if ( m_bActive )
+ {
+ //FIXME: START - Move to client-side
+
+ Vector vecAttachment;
+ QAngle vecAttachmentAngles;
+
+ GetAttachment( 1, vecAttachment, vecAttachmentAngles );
+ g_pEffects->Sparks( vecAttachment );
+
+ //FIXME: END - Move to client-side
+
+ EmitSound( "Weapon_StunStick.Activate" );
+ }
+ else
+ {
+ EmitSound( "Weapon_StunStick.Deactivate" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponStunStick::Deploy( void )
+{
+ SetStunState( true );
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponStunStick::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ if ( BaseClass::Holster( pSwitchingTo ) == false )
+ return false;
+
+ SetStunState( false );
+ SetWeaponVisible( false );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &vecVelocity -
+//-----------------------------------------------------------------------------
+void CWeaponStunStick::Drop( const Vector &vecVelocity )
+{
+ SetStunState( false );
+
+#ifndef CLIENT_DLL
+ UTIL_Remove( this );
+#endif
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponStunStick::GetStunState( void )
+{
+ return m_bActive;
+}
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the attachment point on a viewmodel that a base weapon is using
+//-----------------------------------------------------------------------------
+bool UTIL_GetWeaponAttachment( C_BaseCombatWeapon *pWeapon, int attachmentID, Vector &absOrigin, QAngle &absAngles )
+{
+ // This is already correct in third-person
+ if ( pWeapon && pWeapon->ShouldDrawUsingViewModel() == false )
+ {
+ return pWeapon->GetAttachment( attachmentID, absOrigin, absAngles );
+ }
+
+ // Otherwise we need to translate the attachment to the viewmodel's version and reformat it
+ CBasePlayer *pOwner = ToBasePlayer( pWeapon->GetOwner() );
+
+ if ( pOwner != NULL )
+ {
+ int ret = pOwner->GetViewModel()->GetAttachment( attachmentID, absOrigin, absAngles );
+ FormatViewModelAttachment( absOrigin, true );
+
+ return ret;
+ }
+
+ // Wasn't found
+ return false;
+}
+
+#define BEAM_ATTACH_CORE_NAME "sparkrear"
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets up the attachment point lookup for the model
+//-----------------------------------------------------------------------------
+void C_WeaponStunStick::SetupAttachmentPoints( void )
+{
+ // Setup points for both types of views
+ if ( ShouldDrawUsingViewModel() )
+ {
+ const char *szBeamAttachNamesTop[NUM_BEAM_ATTACHMENTS] =
+ {
+ "spark1a","spark2a","spark3a","spark4a",
+ "spark5a","spark6a","spark7a","spark8a",
+ "spark9a",
+ };
+
+ const char *szBeamAttachNamesBottom[NUM_BEAM_ATTACHMENTS] =
+ {
+ "spark1b","spark2b","spark3b","spark4b",
+ "spark5b","spark6b","spark7b","spark8b",
+ "spark9b",
+ };
+
+ // Lookup and store all connections
+ for ( int i = 0; i < NUM_BEAM_ATTACHMENTS; i++ )
+ {
+ m_BeamAttachments[i].IDs[0] = LookupAttachment( szBeamAttachNamesTop[i] );
+ m_BeamAttachments[i].IDs[1] = LookupAttachment( szBeamAttachNamesBottom[i] );
+ }
+
+ // Setup the center beam point
+ m_BeamCenterAttachment = LookupAttachment( BEAM_ATTACH_CORE_NAME );
+ }
+ else
+ {
+ // Setup the center beam point
+ m_BeamCenterAttachment = 1;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the stunstick model (with extra effects)
+//-----------------------------------------------------------------------------
+int C_WeaponStunStick::DrawModel( int flags )
+{
+ if ( ShouldDraw() == false )
+ return 0;
+
+ // Only render these on the transparent pass
+ if ( flags & STUDIO_TRANSPARENCY )
+ {
+ DrawEffects();
+ return 1;
+ }
+
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Randomly adds extra effects
+//-----------------------------------------------------------------------------
+void C_WeaponStunStick::ClientThink( void )
+{
+ if ( InSwing() == false )
+ {
+ if ( m_bSwungLastFrame )
+ {
+ // Start fading
+ m_flFadeTime = gpGlobals->curtime;
+ m_bSwungLastFrame = false;
+ }
+
+ return;
+ }
+
+ // Remember if we were swinging last frame
+ m_bSwungLastFrame = InSwing();
+
+ if ( IsEffectActive( EF_NODRAW ) )
+ return;
+
+ if ( ShouldDrawUsingViewModel() )
+ {
+ // Update our effects
+ if ( gpGlobals->frametime != 0.0f && ( random->RandomInt( 0, 3 ) == 0 ) )
+ {
+ Vector vecOrigin;
+ QAngle vecAngles;
+
+ // Inner beams
+ BeamInfo_t beamInfo;
+
+ int attachment = random->RandomInt( 0, 15 );
+
+ UTIL_GetWeaponAttachment( this, attachment, vecOrigin, vecAngles );
+ ::FormatViewModelAttachment( vecOrigin, false );
+
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ CBaseEntity *pBeamEnt = pOwner->GetViewModel();
+
+ beamInfo.m_vecStart = vec3_origin;
+ beamInfo.m_pStartEnt= pBeamEnt;
+ beamInfo.m_nStartAttachment = attachment;
+
+ beamInfo.m_pEndEnt = NULL;
+ beamInfo.m_nEndAttachment = -1;
+ beamInfo.m_vecEnd = vecOrigin + RandomVector( -8, 8 );
+
+ beamInfo.m_pszModelName = STUNSTICK_BEAM_MATERIAL;
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.05f;
+ beamInfo.m_flWidth = random->RandomFloat( 1.0f, 2.0f );
+ beamInfo.m_flEndWidth = 0;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = random->RandomFloat( 16, 32 );
+ beamInfo.m_flBrightness = 255.0;
+ beamInfo.m_flSpeed = 0.0;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 1.0f;
+ beamInfo.m_flRed = 255.0f;;
+ beamInfo.m_flGreen = 255.0f;
+ beamInfo.m_flBlue = 255.0f;
+ beamInfo.m_nSegments = 16;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = 0;
+
+ beams->CreateBeamEntPoint( beamInfo );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Starts the client-side version thinking
+//-----------------------------------------------------------------------------
+void C_WeaponStunStick::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ SetupAttachmentPoints();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tells us we're always a translucent entity
+//-----------------------------------------------------------------------------
+RenderGroup_t C_WeaponStunStick::GetRenderGroup( void )
+{
+ return RENDER_GROUP_TWOPASS;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Tells us we're always a translucent entity
+//-----------------------------------------------------------------------------
+bool C_WeaponStunStick::InSwing( void )
+{
+ int activity = GetActivity();
+
+ // FIXME: This is needed until the actual animation works
+ if ( ShouldDrawUsingViewModel() == false )
+ return true;
+
+ // These are the swing activities this weapon can play
+ if ( activity == GetPrimaryAttackActivity() ||
+ activity == GetSecondaryAttackActivity() ||
+ activity == ACT_VM_MISSCENTER ||
+ activity == ACT_VM_MISSCENTER2 )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw our special effects
+//-----------------------------------------------------------------------------
+void C_WeaponStunStick::DrawThirdPersonEffects( void )
+{
+ Vector vecOrigin;
+ QAngle vecAngles;
+ float color[3];
+ float scale;
+
+ CMatRenderContextPtr pRenderContext( materials );
+ IMaterial *pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL, NULL, false );
+ pRenderContext->Bind( pMaterial );
+
+ // Get bright when swung
+ if ( InSwing() )
+ {
+ color[0] = color[1] = color[2] = 0.4f;
+ scale = 22.0f;
+ }
+ else
+ {
+ color[0] = color[1] = color[2] = 0.1f;
+ scale = 20.0f;
+ }
+
+ // Draw an all encompassing glow around the entire head
+ UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles );
+ DrawHalo( pMaterial, vecOrigin, scale, color );
+
+ if ( InSwing() )
+ {
+ pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL2, NULL, false );
+ pRenderContext->Bind( pMaterial );
+
+ color[0] = color[1] = color[2] = random->RandomFloat( 0.6f, 0.8f );
+ scale = random->RandomFloat( 4.0f, 6.0f );
+
+ // Draw an all encompassing glow around the entire head
+ UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles );
+ DrawHalo( pMaterial, vecOrigin, scale, color );
+
+ // Update our effects
+ if ( gpGlobals->frametime != 0.0f && ( random->RandomInt( 0, 5 ) == 0 ) )
+ {
+ Vector vecOrigin;
+ QAngle vecAngles;
+
+ GetAttachment( 1, vecOrigin, vecAngles );
+
+ Vector vForward;
+ AngleVectors( vecAngles, &vForward );
+
+ Vector vEnd = vecOrigin - vForward * 1.0f;
+
+ // Inner beams
+ BeamInfo_t beamInfo;
+
+ beamInfo.m_vecStart = vEnd;
+ Vector offset = RandomVector( -12, 8 );
+
+ offset += Vector(4,4,4);
+ beamInfo.m_vecEnd = vecOrigin + offset;
+
+ beamInfo.m_pStartEnt= cl_entitylist->GetEnt( BEAMENT_ENTITY( entindex() ) );
+ beamInfo.m_pEndEnt = cl_entitylist->GetEnt( BEAMENT_ENTITY( entindex() ) );
+ beamInfo.m_nStartAttachment = 1;
+ beamInfo.m_nEndAttachment = -1;
+
+ beamInfo.m_nType = TE_BEAMTESLA;
+ beamInfo.m_pszModelName = STUNSTICK_BEAM_MATERIAL;
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.01f;
+ beamInfo.m_flWidth = random->RandomFloat( 1.0f, 3.0f );
+ beamInfo.m_flEndWidth = 0;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = random->RandomFloat( 1, 2 );
+ beamInfo.m_flBrightness = 255.0;
+ beamInfo.m_flSpeed = 0.0;
+ beamInfo.m_nStartFrame = 0.0;
+ beamInfo.m_flFrameRate = 1.0f;
+ beamInfo.m_flRed = 255.0f;;
+ beamInfo.m_flGreen = 255.0f;
+ beamInfo.m_flBlue = 255.0f;
+ beamInfo.m_nSegments = 16;
+ beamInfo.m_bRenderable = true;
+ beamInfo.m_nFlags = FBEAM_SHADEOUT;
+
+ beams->CreateBeamPoints( beamInfo );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw our special effects
+//-----------------------------------------------------------------------------
+void C_WeaponStunStick::DrawFirstPersonEffects( void )
+{
+ Vector vecOrigin;
+ QAngle vecAngles;
+ float color[3];
+ float scale;
+
+ CMatRenderContextPtr pRenderContext( materials );
+ IMaterial *pMaterial = materials->FindMaterial( STUNSTICK_GLOW_MATERIAL_NOZ, NULL, false );
+ // FIXME: Needs to work with new IMaterial system!
+ pRenderContext->Bind( pMaterial );
+
+ // Find where we are in the fade
+ float fadeAmount = RemapValClamped( gpGlobals->curtime, m_flFadeTime, m_flFadeTime + FADE_DURATION, 1.0f, 0.1f );
+
+ // Get bright when swung
+ if ( InSwing() )
+ {
+ color[0] = color[1] = color[2] = 0.4f;
+ scale = 22.0f;
+ }
+ else
+ {
+ color[0] = color[1] = color[2] = 0.4f * fadeAmount;
+ scale = 20.0f;
+ }
+
+ if ( color[0] > 0.0f )
+ {
+ // Draw an all encompassing glow around the entire head
+ UTIL_GetWeaponAttachment( this, m_BeamCenterAttachment, vecOrigin, vecAngles );
+ DrawHalo( pMaterial, vecOrigin, scale, color );
+ }
+
+ // Draw bright points at each attachment location
+ for ( int i = 0; i < (NUM_BEAM_ATTACHMENTS*2)+1; i++ )
+ {
+ if ( InSwing() )
+ {
+ color[0] = color[1] = color[2] = random->RandomFloat( 0.05f, 0.5f );
+ scale = random->RandomFloat( 4.0f, 5.0f );
+ }
+ else
+ {
+ color[0] = color[1] = color[2] = random->RandomFloat( 0.05f, 0.5f ) * fadeAmount;
+ scale = random->RandomFloat( 4.0f, 5.0f ) * fadeAmount;
+ }
+
+ if ( color[0] > 0.0f )
+ {
+ UTIL_GetWeaponAttachment( this, i, vecOrigin, vecAngles );
+ DrawHalo( pMaterial, vecOrigin, scale, color );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw our special effects
+//-----------------------------------------------------------------------------
+void C_WeaponStunStick::DrawEffects( void )
+{
+ if ( ShouldDrawUsingViewModel() )
+ {
+ DrawFirstPersonEffects();
+ }
+ else
+ {
+ DrawThirdPersonEffects();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Viewmodel was drawn
+//-----------------------------------------------------------------------------
+void C_WeaponStunStick::ViewModelDrawn( C_BaseViewModel *pBaseViewModel )
+{
+ // Don't bother when we're not deployed
+ if ( IsWeaponVisible() )
+ {
+ // Do all our special effects
+ DrawEffects();
+ }
+
+ BaseClass::ViewModelDrawn( pBaseViewModel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw a cheap glow quad at our impact point (with sparks)
+//-----------------------------------------------------------------------------
+void StunstickImpactCallback( const CEffectData &data )
+{
+ float scale = random->RandomFloat( 16, 32 );
+
+ FX_AddQuad( data.m_vOrigin,
+ data.m_vNormal,
+ scale,
+ scale*2.0f,
+ 1.0f,
+ 1.0f,
+ 0.0f,
+ 0.0f,
+ random->RandomInt( 0, 360 ),
+ 0,
+ Vector( 1.0f, 1.0f, 1.0f ),
+ 0.1f,
+ "sprites/light_glow02_add",
+ 0 );
+
+ FX_Sparks( data.m_vOrigin, 1, 2, data.m_vNormal, 6, 64, 256 );
+}
+
+DECLARE_CLIENT_EFFECT( "StunstickImpact", StunstickImpactCallback );
+
#endif \ No newline at end of file