summaryrefslogtreecommitdiff
path: root/game/shared/tf2
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/tf2')
-rw-r--r--game/shared/tf2/baseobject_shared.cpp583
-rw-r--r--game/shared/tf2/baseobject_shared.h49
-rw-r--r--game/shared/tf2/basetfcombatweapon_shared.cpp497
-rw-r--r--game/shared/tf2/basetfcombatweapon_shared.h152
-rw-r--r--game/shared/tf2/basetfplayer_shared.cpp825
-rw-r--r--game/shared/tf2/basetfplayer_shared.h22
-rw-r--r--game/shared/tf2/basetfvehicle.cpp1179
-rw-r--r--game/shared/tf2/basetfvehicle.h272
-rw-r--r--game/shared/tf2/env_laserdesignation.cpp328
-rw-r--r--game/shared/tf2/env_laserdesignation.h80
-rw-r--r--game/shared/tf2/gasoline_shared.h40
-rw-r--r--game/shared/tf2/grenade_antipersonnel.cpp211
-rw-r--r--game/shared/tf2/grenade_antipersonnel.h50
-rw-r--r--game/shared/tf2/grenade_base_empable.cpp89
-rw-r--r--game/shared/tf2/grenade_base_empable.h50
-rw-r--r--game/shared/tf2/grenade_emp.cpp343
-rw-r--r--game/shared/tf2/grenade_emp.h82
-rw-r--r--game/shared/tf2/grenade_limpetmine.cpp406
-rw-r--r--game/shared/tf2/grenade_limpetmine.h70
-rw-r--r--game/shared/tf2/grenade_objectsapper.cpp211
-rw-r--r--game/shared/tf2/grenade_objectsapper.h50
-rw-r--r--game/shared/tf2/grenade_rocket.cpp176
-rw-r--r--game/shared/tf2/grenade_rocket.h60
-rw-r--r--game/shared/tf2/grenade_stickybomb.cpp129
-rw-r--r--game/shared/tf2/ihasbuildpoints.h60
-rw-r--r--game/shared/tf2/plasmaprojectile.cpp843
-rw-r--r--game/shared/tf2/plasmaprojectile.h245
-rw-r--r--game/shared/tf2/plasmaprojectile_shared.cpp72
-rw-r--r--game/shared/tf2/plasmaprojectile_shared.h48
-rw-r--r--game/shared/tf2/techtree.cpp1173
-rw-r--r--game/shared/tf2/techtree.h320
-rw-r--r--game/shared/tf2/techtree_parse.cpp183
-rw-r--r--game/shared/tf2/tf_gamemovement.cpp2149
-rw-r--r--game/shared/tf2/tf_gamemovement.h126
-rw-r--r--game/shared/tf2/tf_gamemovement_chooser.cpp76
-rw-r--r--game/shared/tf2/tf_gamemovement_chooser.h69
-rw-r--r--game/shared/tf2/tf_gamemovement_commando.cpp424
-rw-r--r--game/shared/tf2/tf_gamemovement_commando.h64
-rw-r--r--game/shared/tf2/tf_gamemovement_defender.cpp65
-rw-r--r--game/shared/tf2/tf_gamemovement_defender.h50
-rw-r--r--game/shared/tf2/tf_gamemovement_escort.cpp65
-rw-r--r--game/shared/tf2/tf_gamemovement_escort.h50
-rw-r--r--game/shared/tf2/tf_gamemovement_infiltrator.cpp62
-rw-r--r--game/shared/tf2/tf_gamemovement_infiltrator.h50
-rw-r--r--game/shared/tf2/tf_gamemovement_medic.cpp62
-rw-r--r--game/shared/tf2/tf_gamemovement_medic.h50
-rw-r--r--game/shared/tf2/tf_gamemovement_pyro.cpp63
-rw-r--r--game/shared/tf2/tf_gamemovement_pyro.h50
-rw-r--r--game/shared/tf2/tf_gamemovement_recon.cpp595
-rw-r--r--game/shared/tf2/tf_gamemovement_recon.h73
-rw-r--r--game/shared/tf2/tf_gamemovement_sapper.cpp62
-rw-r--r--game/shared/tf2/tf_gamemovement_sapper.h50
-rw-r--r--game/shared/tf2/tf_gamemovement_sniper.cpp62
-rw-r--r--game/shared/tf2/tf_gamemovement_sniper.h50
-rw-r--r--game/shared/tf2/tf_gamemovement_support.cpp62
-rw-r--r--game/shared/tf2/tf_gamemovement_support.h50
-rw-r--r--game/shared/tf2/tf_gamerules.cpp2242
-rw-r--r--game/shared/tf2/tf_gamerules.h142
-rw-r--r--game/shared/tf2/tf_hints.h34
-rw-r--r--game/shared/tf2/tf_movedata.h71
-rw-r--r--game/shared/tf2/tf_obj_base_manned_gun.cpp680
-rw-r--r--game/shared/tf2/tf_obj_base_manned_gun.h169
-rw-r--r--game/shared/tf2/tf_obj_basedrivergun_shared.cpp92
-rw-r--r--game/shared/tf2/tf_obj_basedrivergun_shared.h64
-rw-r--r--game/shared/tf2/tf_obj_baseupgrade_shared.cpp58
-rw-r--r--game/shared/tf2/tf_obj_baseupgrade_shared.h41
-rw-r--r--game/shared/tf2/tf_obj_driver_machinegun_shared.cpp219
-rw-r--r--game/shared/tf2/tf_obj_driver_machinegun_shared.h62
-rw-r--r--game/shared/tf2/tf_obj_manned_plasmagun.cpp304
-rw-r--r--game/shared/tf2/tf_obj_manned_plasmagun.h88
-rw-r--r--game/shared/tf2/tf_obj_manned_plasmagun_shared.cpp117
-rw-r--r--game/shared/tf2/tf_obj_manned_plasmagun_shared.h50
-rw-r--r--game/shared/tf2/tf_playeranimstate.h76
-rw-r--r--game/shared/tf2/tf_reconvars.cpp64
-rw-r--r--game/shared/tf2/tf_reconvars.h31
-rw-r--r--game/shared/tf2/tf_shareddefs.cpp605
-rw-r--r--game/shared/tf2/tf_shareddefs.h663
-rw-r--r--game/shared/tf2/tf_shield_mobile_shared.cpp850
-rw-r--r--game/shared/tf2/tf_shieldshared.cpp579
-rw-r--r--game/shared/tf2/tf_shieldshared.h262
-rw-r--r--game/shared/tf2/tf_tacticalmap.cpp122
-rw-r--r--game/shared/tf2/tf_usermessages.cpp43
-rw-r--r--game/shared/tf2/tf_vehicleshared.h47
-rw-r--r--game/shared/tf2/tfclassdata_shared.cpp58
-rw-r--r--game/shared/tf2/tfclassdata_shared.h368
-rw-r--r--game/shared/tf2/vehicle_mortar_shared.h18
-rw-r--r--game/shared/tf2/weapon_arcwelder.cpp322
-rw-r--r--game/shared/tf2/weapon_basecombatobject.cpp126
-rw-r--r--game/shared/tf2/weapon_basecombatobject.h68
-rw-r--r--game/shared/tf2/weapon_builder.cpp530
-rw-r--r--game/shared/tf2/weapon_builder.h90
-rw-r--r--game/shared/tf2/weapon_combat_basegrenade.cpp152
-rw-r--r--game/shared/tf2/weapon_combat_basegrenade.h66
-rw-r--r--game/shared/tf2/weapon_combat_burstrifle.cpp327
-rw-r--r--game/shared/tf2/weapon_combat_chargeableplasma.cpp322
-rw-r--r--game/shared/tf2/weapon_combat_grenade.cpp97
-rw-r--r--game/shared/tf2/weapon_combat_grenade_emp.cpp92
-rw-r--r--game/shared/tf2/weapon_combat_laserrifle.cpp375
-rw-r--r--game/shared/tf2/weapon_combat_plasma_grenade_launcher.cpp195
-rw-r--r--game/shared/tf2/weapon_combat_plasmarifle.cpp559
-rw-r--r--game/shared/tf2/weapon_combat_shotgun.cpp318
-rw-r--r--game/shared/tf2/weapon_combat_usedwithshieldbase.cpp111
-rw-r--r--game/shared/tf2/weapon_combat_usedwithshieldbase.h59
-rw-r--r--game/shared/tf2/weapon_combatshield.cpp1205
-rw-r--r--game/shared/tf2/weapon_combatshield.h185
-rw-r--r--game/shared/tf2/weapon_drainbeam.cpp523
-rw-r--r--game/shared/tf2/weapon_drainbeam.h99
-rw-r--r--game/shared/tf2/weapon_flame_thrower.cpp434
-rw-r--r--game/shared/tf2/weapon_flame_thrower.h95
-rw-r--r--game/shared/tf2/weapon_gas_can.cpp35
-rw-r--r--game/shared/tf2/weapon_gas_can.h39
-rw-r--r--game/shared/tf2/weapon_grenade_rocket.cpp390
-rw-r--r--game/shared/tf2/weapon_grenade_rocket.h82
-rw-r--r--game/shared/tf2/weapon_harpoon.cpp735
-rw-r--r--game/shared/tf2/weapon_infiltrator.cpp150
-rw-r--r--game/shared/tf2/weapon_infiltrator.h36
-rw-r--r--game/shared/tf2/weapon_laserdesignator.cpp394
-rw-r--r--game/shared/tf2/weapon_laserrifle.cpp160
-rw-r--r--game/shared/tf2/weapon_limpetmine.cpp688
-rw-r--r--game/shared/tf2/weapon_limpetmine.h109
-rw-r--r--game/shared/tf2/weapon_minigun.cpp415
-rw-r--r--game/shared/tf2/weapon_mortar.cpp367
-rw-r--r--game/shared/tf2/weapon_mortar.h84
-rw-r--r--game/shared/tf2/weapon_obj_empgenerator.cpp37
-rw-r--r--game/shared/tf2/weapon_obj_rallyflag.cpp74
-rw-r--r--game/shared/tf2/weapon_objectselection.cpp216
-rw-r--r--game/shared/tf2/weapon_objectselection.h69
-rw-r--r--game/shared/tf2/weapon_pistols.cpp134
-rw-r--r--game/shared/tf2/weapon_plasmarifle.cpp74
-rw-r--r--game/shared/tf2/weapon_repairgun.cpp740
-rw-r--r--game/shared/tf2/weapon_repairgun.h112
-rw-r--r--game/shared/tf2/weapon_rocketlauncher.cpp211
-rw-r--r--game/shared/tf2/weapon_rocketlauncher.h58
-rw-r--r--game/shared/tf2/weapon_shield.cpp218
-rw-r--r--game/shared/tf2/weapon_shield.h83
-rw-r--r--game/shared/tf2/weapon_shieldgrenade.cpp211
-rw-r--r--game/shared/tf2/weapon_twohandedcontainer.cpp395
-rw-r--r--game/shared/tf2/weapon_twohandedcontainer.h97
138 files changed, 34359 insertions, 0 deletions
diff --git a/game/shared/tf2/baseobject_shared.cpp b/game/shared/tf2/baseobject_shared.cpp
new file mode 100644
index 0000000..5c77335
--- /dev/null
+++ b/game/shared/tf2/baseobject_shared.cpp
@@ -0,0 +1,583 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "baseobject_shared.h"
+#include <KeyValues.h>
+#include "tf_shareddefs.h"
+#include "engine/ivmodelinfo.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse our model and create the buildpoints in it
+//-----------------------------------------------------------------------------
+void CBaseObject::CreateBuildPoints( void )
+{
+ // Clear out any existing build points
+ m_BuildPoints.RemoveAll();
+
+ KeyValues * modelKeyValues = new KeyValues("");
+ if ( !modelKeyValues->LoadFromBuffer( modelinfo->GetModelName( GetModel() ), modelinfo->GetModelKeyValueText( GetModel() ) ) )
+ {
+ return;
+ }
+
+ // Do we have a build point section?
+ KeyValues *pkvAllBuildPoints = modelKeyValues->FindKey("build_points");
+ if ( pkvAllBuildPoints )
+ {
+ KeyValues *pkvBuildPoint = pkvAllBuildPoints->GetFirstSubKey();
+ while ( pkvBuildPoint )
+ {
+ // Find the attachment first
+ const char *sAttachment = pkvBuildPoint->GetName();
+ int iAttachmentNumber = LookupAttachment( sAttachment );
+ if ( iAttachmentNumber )
+ {
+ AddAndParseBuildPoint( iAttachmentNumber, pkvBuildPoint );
+ }
+ else
+ {
+ Msg( "ERROR: Model %s specifies buildpoint %s, but has no attachment named %s.\n", STRING(GetModelName()), pkvBuildPoint->GetString(), pkvBuildPoint->GetString() );
+ }
+
+ pkvBuildPoint = pkvBuildPoint->GetNextKey();
+ }
+ }
+
+ // Any virtual build points (build points that aren't on an attachment)?
+ pkvAllBuildPoints = modelKeyValues->FindKey("virtual_build_points");
+ if ( pkvAllBuildPoints )
+ {
+ KeyValues *pkvBuildPoint = pkvAllBuildPoints->GetFirstSubKey();
+ while ( pkvBuildPoint )
+ {
+ AddAndParseBuildPoint( -1, pkvBuildPoint );
+ pkvBuildPoint = pkvBuildPoint->GetNextKey();
+ }
+ }
+
+ modelKeyValues->deleteThis();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObject::AddAndParseBuildPoint( int iAttachmentNumber, KeyValues *pkvBuildPoint )
+{
+ int iPoint = AddBuildPoint( iAttachmentNumber );
+
+
+ m_BuildPoints[iPoint].m_bPutInAttachmentSpace = (pkvBuildPoint->GetInt( "PutInAttachmentSpace", 0 ) != 0);
+
+ // Now see if we've got a set of valid objects specified
+ KeyValues *pkvValidObjects = pkvBuildPoint->FindKey( "valid_objects" );
+ if ( pkvValidObjects )
+ {
+ KeyValues *pkvObject = pkvValidObjects->GetFirstSubKey();
+ while ( pkvObject )
+ {
+ const char *pSpecifiedObject = pkvObject->GetName();
+ int iLenObjName = Q_strlen( pSpecifiedObject );
+
+ // Find the object index for the name
+ for ( int i = 0; i < OBJ_LAST; i++ )
+ {
+ if ( !Q_strncmp( GetObjectInfo( i )->m_pClassName, pSpecifiedObject, iLenObjName) )
+ {
+ AddValidObjectToBuildPoint( iPoint, i );
+ break;
+ }
+ }
+
+ pkvObject = pkvObject->GetNextKey();
+ }
+ }
+
+ SetBuildPointPassenger( iPoint, pkvBuildPoint->GetInt( "passenger", -1 ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a new buildpoint to my list of buildpoints
+//-----------------------------------------------------------------------------
+int CBaseObject::AddBuildPoint( int iAttachmentNum )
+{
+ // Make a new buildpoint
+ BuildPoint_t sNewPoint;
+ sNewPoint.m_hObject = NULL;
+ sNewPoint.m_iAttachmentNum = iAttachmentNum;
+ sNewPoint.m_iPassenger = -1;
+ sNewPoint.m_bPutInAttachmentSpace = false;
+ Q_memset( sNewPoint.m_bValidObjects, 0, sizeof( sNewPoint.m_bValidObjects ) );
+
+ // Insert it into our list
+ return m_BuildPoints.AddToTail( sNewPoint );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Indicate which passenger position this build point is associated with
+//-----------------------------------------------------------------------------
+void CBaseObject::SetBuildPointPassenger( int iPoint, int iPassenger )
+{
+ m_BuildPoints[iPoint].m_iPassenger = iPassenger;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseObject::GetBuildPointPassenger( int iPoint ) const
+{
+ return m_BuildPoints[iPoint].m_iPassenger;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObject::AddValidObjectToBuildPoint( int iPoint, int iObjectType )
+{
+ Assert( iPoint <= GetNumBuildPoints() );
+ m_BuildPoints[iPoint].m_bValidObjects[ iObjectType ] = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseObject::GetNumBuildPoints( void ) const
+{
+ return m_BuildPoints.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseObject* CBaseObject::GetBuildPointObject( int iPoint )
+{
+ Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
+
+ return m_BuildPoints[iPoint].m_hObject;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the specified object type can be built on this point
+//-----------------------------------------------------------------------------
+bool CBaseObject::CanBuildObjectOnBuildPoint( int iPoint, int iObjectType )
+{
+ Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
+
+ // Allowed to build here?
+ if ( !m_BuildPoints[iPoint].m_bValidObjects[ iObjectType ] )
+ return false;
+
+ // Buildpoint empty?
+ return ( m_BuildPoints[iPoint].m_hObject == NULL );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseObject::GetBuildPoint( int iPoint, Vector &vecOrigin, QAngle &vecAngles )
+{
+ Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
+
+ int iAttachmentNum = m_BuildPoints[iPoint].m_iAttachmentNum;
+ if ( iAttachmentNum == -1 )
+ {
+ vecOrigin = GetAbsOrigin();
+ vecAngles = GetAbsAngles();
+ return true;
+ }
+ else
+ {
+ return GetAttachment( m_BuildPoints[iPoint].m_iAttachmentNum, vecOrigin, vecAngles );
+ }
+}
+
+
+int CBaseObject::GetBuildPointAttachmentIndex( int iPoint ) const
+{
+ Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
+
+ if ( m_BuildPoints[iPoint].m_bPutInAttachmentSpace )
+ {
+ return m_BuildPoints[iPoint].m_iAttachmentNum;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObject::SetObjectOnBuildPoint( int iPoint, CBaseObject *pObject )
+{
+ Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
+ m_BuildPoints[iPoint].m_hObject = pObject;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CBaseObject::GetMaxSnapDistance( int iPoint )
+{
+ Assert( iPoint >= 0 && iPoint <= GetNumBuildPoints() );
+
+ if ( m_BuildPoints[iPoint].m_iAttachmentNum == -1 )
+ {
+ // Virtual build points need some more space since they represent an upgrade to the whole object.
+ return 128;
+ }
+ else
+ {
+ return 128;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the number of objects on my build points
+//-----------------------------------------------------------------------------
+int CBaseObject::GetNumObjectsOnMe( void )
+{
+ int iObjects = 0;
+ for ( int i = 0; i < GetNumBuildPoints(); i++ )
+ {
+ if ( m_BuildPoints[i].m_hObject )
+ {
+ iObjects++;
+ }
+ }
+
+ return iObjects;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the first object build on this object
+//-----------------------------------------------------------------------------
+CBaseEntity *CBaseObject::GetFirstObjectOnMe( void )
+{
+ for ( int i = 0; i < GetNumBuildPoints(); i++ )
+ {
+ if ( m_BuildPoints[i].m_hObject )
+ return m_BuildPoints[i].m_hObject;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// I've finished building the specified object on the specified build point
+//-----------------------------------------------------------------------------
+int CBaseObject::FindObjectOnBuildPoint( CBaseObject *pObject )
+{
+ for (int i = m_BuildPoints.Count(); --i >= 0; )
+ {
+ if (m_BuildPoints[i].m_hObject == pObject)
+ return i;
+ }
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseObject *CBaseObject::GetObjectOfTypeOnMe( int iObjectType )
+{
+ for ( int iObject = 0; iObject < GetNumObjectsOnMe(); ++iObject )
+ {
+ CBaseObject *pObject = dynamic_cast<CBaseObject*>( m_BuildPoints[iObject].m_hObject.Get() );
+ if ( pObject )
+ {
+ if ( pObject->GetType() == iObjectType )
+ return pObject;
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObject::RemoveAllObjects( void )
+{
+ for ( int i = 0; i < GetNumBuildPoints(); i++ )
+ {
+ if ( m_BuildPoints[i].m_hObject )
+ {
+#ifndef CLIENT_DLL
+ UTIL_Remove( m_BuildPoints[i].m_hObject );
+#endif
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseObject *CBaseObject::GetParentObject( void )
+{
+ if ( GetMoveParent() )
+ return dynamic_cast<CBaseObject*>(GetMoveParent());
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if I ever accept this powerup type
+//-----------------------------------------------------------------------------
+bool CBaseObject::CanPowerupEver( int iPowerup )
+{
+ Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
+
+ // Un-repairable objects can't be boosted
+ switch( iPowerup )
+ {
+#ifndef CLIENT_DLL
+ case POWERUP_BOOST:
+ if ( !m_flRepairMultiplier )
+ return false;
+ break;
+#endif
+
+ case POWERUP_POWER:
+ // Do we use power?
+ if ( m_fObjectFlags & OF_DOESNT_NEED_POWER )
+ return false;
+
+ // Objects on vehicles never need power
+ if ( IsBuiltOnAttachment() )
+ {
+ if ( GetParentObject() && GetParentObject()->IsAVehicle() )
+ return false;
+ }
+
+ // Ok, I'll be needing some juice
+ return true;
+
+ default:
+ break;
+ }
+
+ // Don't accept any powerups if we're placing
+ if ( IsPlacing() )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if I can be powered by this powerup right now
+//-----------------------------------------------------------------------------
+bool CBaseObject::CanPowerupNow( int iPowerup )
+{
+ Assert( iPowerup >= 0 && iPowerup < MAX_POWERUPS );
+
+ if ( !CanPowerupEver(iPowerup) )
+ return false;
+
+ // Un-repairable objects can't be boosted
+ switch( iPowerup )
+ {
+ case POWERUP_POWER:
+ // If I have power, I don't need it
+ if ( IsPowered() )
+ return false;
+ return true;
+
+ default:
+ break;
+ }
+
+ // Don't accept any powerups if we're placing
+ if ( IsPlacing() )
+ return false;
+
+#ifdef CLIENT_DLL
+ return true;
+#else
+ return BaseClass::CanPowerupEver( iPowerup );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this object has power
+//-----------------------------------------------------------------------------
+bool CBaseObject::IsPowered( void )
+{
+ if ( !CanPowerupEver( POWERUP_POWER ) )
+ return true;
+
+#ifdef CLIENT_DLL
+ return ( HasPowerup(POWERUP_POWER) );
+#else
+ return ( HasPowerup(POWERUP_POWER) || m_hPowerPack );
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CBaseObject::GetSapperAttachTime( void )
+{
+ return GetObjectInfo( GetType() )->m_flSapperAttachTime;
+}
+
+static ConVar sv_ignore_hitboxes( "sv_ignore_hitboxes", "0", FCVAR_REPLICATED, "Disable hitboxes" );
+
+bool CBaseObject::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
+{
+ bool bReturn = BaseClass::TestHitboxes( ray, fContentsMask, tr );
+
+ if( !sv_ignore_hitboxes.GetBool() )
+ return bReturn;
+
+
+ if( !bReturn )
+ {
+ return false;
+ }
+
+ if( tr.fraction == 1.f && !tr.allsolid && !tr.startsolid )
+ {
+ return false;
+ }
+
+ return bReturn;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this object should be active
+//-----------------------------------------------------------------------------
+bool CBaseObject::ShouldBeActive( void )
+{
+ // Placing and/or constructing objects shouldn't be active
+ if ( IsPlacing() || IsBuilding() )
+ return false;
+
+ // Powered? Or don't need it
+ if ( CanPowerupEver(POWERUP_POWER) && !HasPowerup( POWERUP_POWER ) )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the object's type
+//-----------------------------------------------------------------------------
+void CBaseObject::SetType( int iObjectType )
+{
+ m_iObjectType = iObjectType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : act -
+//-----------------------------------------------------------------------------
+void CBaseObject::SetActivity( Activity act )
+{
+ // Allow any model swapping, etc. to occur
+ OnActivityChanged( act );
+
+ // Hrm, it's not actually a studio model...
+ if ( !GetModelPtr() )
+ return;
+
+ int sequence = SelectWeightedSequence( act );
+ if ( sequence != ACTIVITY_NOT_AVAILABLE )
+ {
+ m_Activity = act;
+ SetObjectSequence( sequence );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Activity
+//-----------------------------------------------------------------------------
+Activity CBaseObject::GetActivity( ) const
+{
+ return m_Activity;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : act -
+//-----------------------------------------------------------------------------
+void CBaseObject::OnActivityChanged( Activity act )
+{
+ // Nothing
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Thin wrapper over CBaseAnimating::SetSequence to do bookkeeping.
+// Input : sequence -
+//-----------------------------------------------------------------------------
+void CBaseObject::SetObjectSequence( int sequence )
+{
+ ResetSequence( sequence );
+ SetCycle( 0 );
+
+#if !defined( CLIENT_DLL )
+ if ( IsUsingClientSideAnimation() )
+ {
+ ResetClientsideFrame();
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObject::AttemptToGoActive( void )
+{
+ // Go active if we can
+ if ( ShouldBeActive() )
+ {
+ OnGoActive();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObject::OnGoActive( void )
+{
+#ifndef CLIENT_DLL
+ // Play startup animation
+ PlayStartupAnimation();
+
+ // Switch to the on state
+ if ( GetModelPtr() )
+ {
+ int index = FindBodygroupByName( "powertoggle" );
+ if ( index >= 0 )
+ {
+ SetBodygroup( index, 1 );
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObject::OnGoInactive( void )
+{
+#ifndef CLIENT_DLL
+ if ( GetModelPtr() )
+ {
+ // Switch to the off state
+ int index = FindBodygroupByName( "powertoggle" );
+ if ( index >= 0 )
+ {
+ SetBodygroup( index, 0 );
+ }
+ }
+#endif
+} \ No newline at end of file
diff --git a/game/shared/tf2/baseobject_shared.h b/game/shared/tf2/baseobject_shared.h
new file mode 100644
index 0000000..b556098
--- /dev/null
+++ b/game/shared/tf2/baseobject_shared.h
@@ -0,0 +1,49 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BASEOBJECT_SHARED_H
+#define BASEOBJECT_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_shareddefs.h"
+
+#if defined( CLIENT_DLL )
+#define CBaseObject C_BaseObject
+#endif
+
+class CBaseObject;
+typedef CHandle<CBaseObject> ObjectHandle;
+struct BuildPoint_t
+{
+ // If this is true, then objects are parented to the attachment point instead of
+ // parented to the entity's abs origin + angles. That way, they'll move if the
+ // attachment point animates.
+ bool m_bPutInAttachmentSpace;
+
+ int m_iAttachmentNum;
+ ObjectHandle m_hObject;
+ int m_iPassenger;
+ bool m_bValidObjects[ OBJ_LAST ];
+};
+
+struct VulnerablePoint_t
+{
+ float m_fDamageMultiplier;
+ int m_nSet;
+ int m_nBox;
+};
+
+// Shared header file for players
+#if defined( CLIENT_DLL )
+#include "c_baseobject.h"
+#else
+#include "tf_obj.h"
+#endif
+
+#endif // BASEOBJECT_SHARED_H
diff --git a/game/shared/tf2/basetfcombatweapon_shared.cpp b/game/shared/tf2/basetfcombatweapon_shared.cpp
new file mode 100644
index 0000000..9484284
--- /dev/null
+++ b/game/shared/tf2/basetfcombatweapon_shared.cpp
@@ -0,0 +1,497 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "basetfcombatweapon_shared.h"
+#include "weapon_twohandedcontainer.h"
+
+#if !defined( CLIENT_DLL )
+#include "soundent.h"
+#else
+#include "functionproxy.h"
+#include "ivrenderview.h"
+#include "cl_animevent.h"
+#include "fx.h"
+#endif
+
+CBaseTFCombatWeapon::CBaseTFCombatWeapon ( void )
+{
+ m_bReflectViewModelAnimations = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFCombatWeapon::Precache( void )
+{
+ BaseClass::Precache();
+
+#if !defined( CLIENT_DLL )
+ // Connect to my CVars
+ m_pDamageCVar = cvar->FindVar( UTIL_VarArgs( "%s_damage", GetClassname() ) );
+ m_pRangeCVar = cvar->FindVar( UTIL_VarArgs( "%s_range", GetClassname() ) );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTFCombatWeapon::GetPrimaryAmmo( void )
+{
+ // Get the local player
+ CBasePlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer == NULL )
+ return 0;
+
+ return pPlayer->GetAmmoCount( m_iPrimaryAmmoType );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if the owner is EMPed
+//-----------------------------------------------------------------------------
+bool CBaseTFCombatWeapon::IsOwnerEMPed()
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ((!pPlayer) || (!pPlayer->GetPlayerClass()))
+ return false;
+
+ return ( pPlayer->HasPowerup(POWERUP_EMP) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Temporarily remove disguise
+//-----------------------------------------------------------------------------
+void CBaseTFCombatWeapon::CheckRemoveDisguise( void )
+{
+#if !defined( CLIENT_DLL )
+ CBaseTFPlayer *player = static_cast< CBaseTFPlayer * >( GetOwner());
+ if ( player )
+ {
+ // Always remove camo
+ player->ClearCamouflage();
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Factor in the player's anim speed to the sequence duration for weapons
+//-----------------------------------------------------------------------------
+float CBaseTFCombatWeapon::SequenceDuration( int iSequence )
+{
+ float flDuration = BaseClass::SequenceDuration( iSequence );
+ CBaseTFPlayer *pOwner = (CBaseTFPlayer *)GetOwner();
+ if ( pOwner )
+ {
+ flDuration /= pOwner->GetDefaultAnimSpeed();
+ }
+
+ return flDuration;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Override weapon sound. TF doesn't use ATTN_GUNFIRE for it's weapon attenuations.
+//-----------------------------------------------------------------------------
+void CBaseTFCombatWeapon::WeaponSound( WeaponSound_t shoot_type, float soundtime /*= 0.0f*/ )
+{
+#if !defined( CLIENT_DLL )
+ // HACKHACK: Force a combat sound to alert NPCs, for antlion prototype
+ CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 600, 0.1 );
+#endif
+
+ BaseClass::WeaponSound( shoot_type, soundtime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the point from which to create the weapon's tracer
+//-----------------------------------------------------------------------------
+Vector CBaseTFCombatWeapon::GetTracerSrc( Vector &vecSrc, Vector &vecFireDir )
+{
+ QAngle vecAngles;
+ VectorAngles( vecFireDir, vecAngles );
+ Vector right;
+ AngleVectors( vecAngles, NULL, &right, NULL );
+ return (vecSrc + Vector ( 0,0,-4 ) + right * 2 + vecFireDir * 16);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : activity -
+//-----------------------------------------------------------------------------
+void CBaseTFCombatWeapon::PlayAttackAnimation( int activity )
+{
+ SendWeaponAnim( activity );
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+ pPlayer->SetLastAttackTime( gpGlobals->curtime );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : sequence -
+//-----------------------------------------------------------------------------
+bool CBaseTFCombatWeapon::SendWeaponAnim( int iActivity )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return false;
+
+ CBaseTFCombatWeapon *pOther = NULL;
+
+ // See if we are wielding multiple weapons
+ CWeaponTwoHandedContainer *pContainer = dynamic_cast< CWeaponTwoHandedContainer * >( pPlayer->GetActiveWeapon() );
+ if ( pContainer )
+ {
+ // Make sure they exist
+ CBaseTFCombatWeapon *left = static_cast< CBaseTFCombatWeapon * >( pContainer->GetLeftWeapon() );
+ CBaseTFCombatWeapon *right = static_cast< CBaseTFCombatWeapon * >( pContainer->GetRightWeapon() );
+ if ( !left || !right )
+ return false;
+
+ // Make sure one of them is this!!!
+ if ( left != this && right != this )
+ return false;
+
+ // Get pointer to other one
+ pOther = left;
+ if ( left == this )
+ {
+ pOther = right;
+ }
+
+ Assert(pOther);
+
+ // Now ask our other weapon if it would like to stomp my animation attempt
+ iActivity = pOther->ReplaceOtherWeaponsActivity( iActivity );
+ if ( iActivity == -1 )
+ return false;
+ }
+
+ // Always pass through to base
+ BaseClass::SendWeaponAnim( iActivity );
+
+ if ( !IsReflectingAnimations() )
+ return false;
+
+ // See if we are wielding multiple weapons
+ if ( !pContainer )
+ return false;
+
+ Assert(pOther);
+ // Send our other weapon the activity code
+ iActivity = GetOtherWeaponsActivity( iActivity );
+ if ( iActivity != -1 )
+ {
+ pOther->SendWeaponAnim( iActivity );
+
+ // Remember it for weapon switching
+ m_iLastReflectedActivity = iActivity;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : reflect -
+//-----------------------------------------------------------------------------
+void CBaseTFCombatWeapon::SetReflectViewModelAnimations( bool reflect )
+{
+ m_bReflectViewModelAnimations = reflect;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTFCombatWeapon::IsReflectingAnimations( void ) const
+{
+ return m_bReflectViewModelAnimations;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseTFCombatWeapon::IsCamouflaged( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)GetOwner();
+ if ( pPlayer && pPlayer->IsCamouflaged() )
+ return true;
+
+ return false;
+}
+
+#if defined( CLIENT_DLL )
+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_UNREGISTERED*/ );
+static ConVar v_iroll_cycle( "v_iroll_cycle", "0.5"/*, FCVAR_UNREGISTERED*/ );
+static ConVar v_ipitch_cycle( "v_ipitch_cycle", "1"/*, FCVAR_UNREGISTERED*/ );
+static ConVar v_iyaw_level( "v_iyaw_level", "0.3"/*, FCVAR_UNREGISTERED*/ );
+static ConVar v_iroll_level( "v_iroll_level", "0.1"/*, FCVAR_UNREGISTERED*/ );
+static ConVar v_ipitch_level( "v_ipitch_level", "0.3"/*, FCVAR_UNREGISTERED*/ );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CBaseTFCombatWeapon::CalcViewmodelBob( void )
+{
+ static double bobtime;
+ static float bob;
+ float cycle;
+
+ CBasePlayer *player = ToBasePlayer( GetOwner() );
+ if ( !player )
+ return 0.0f;
+
+ if ( ( player->GetGroundEntity() == NULL ) || !gpGlobals->frametime )
+ {
+ return bob; // just use old value
+ }
+
+ bobtime += gpGlobals->frametime;
+
+ cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat())*cl_bobcycle.GetFloat();
+ cycle /= cl_bobcycle.GetFloat();
+
+ if (cycle < cl_bobup.GetFloat())
+ {
+ cycle = M_PI * cycle / cl_bobup.GetFloat();
+ }
+ else
+ {
+ cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat());
+ }
+
+ // bob is proportional to simulated velocity in the xy plane
+ // (don't count Z, or jumping messes it up)
+ bob = player->GetAbsVelocity().Length2D() * cl_bob.GetFloat();
+
+ bob = bob*0.3 + bob*0.7*sin(cycle);
+
+ bob = MIN( 4.0, bob );
+ bob = MAX( -7.0, bob );
+ return bob;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+void CBaseTFCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
+{
+ float fIdleScale = 2.0f;
+
+ // Bias so view models aren't all synced to each other
+ //float curtime = gpGlobals->curtime + ( viewmodelindex * 2 * M_PI / GetViewModelCount() );
+
+ float curtime = gpGlobals->curtime + ( viewmodel->entindex() * 2 * M_PI );
+
+ origin[ROLL] -= fIdleScale * sin(curtime*v_iroll_cycle.GetFloat()) * v_iroll_level.GetFloat();
+ origin[PITCH] -= fIdleScale * sin(curtime*v_ipitch_cycle.GetFloat()) * (v_ipitch_level.GetFloat() * 0.5);
+ origin[YAW] -= fIdleScale * sin(curtime*v_iyaw_cycle.GetFloat()) * v_iyaw_level.GetFloat();
+
+ Vector forward;
+ AngleVectors( angles, &forward, NULL, NULL );
+
+ float flBob = CalcViewmodelBob();
+
+ // Apply bob, but scaled down to 40%
+ VectorMA( origin, flBob * 0.4, forward, origin );
+
+ // Z bob a bit more
+ origin[2] += flBob;
+
+ // throw in a little tilt.
+ angles[ YAW ] -= flBob * 0.6;
+ angles[ ROLL ] -= flBob * 0.5;
+ angles[ PITCH ] -= flBob * 0.4;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: TF specific weapon anim events
+//-----------------------------------------------------------------------------
+bool CBaseTFCombatWeapon::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ switch( event )
+ {
+ case CL_EVENT_MUZZLEFLASH0:
+ case CL_EVENT_MUZZLEFLASH1:
+ case CL_EVENT_MUZZLEFLASH2:
+ case CL_EVENT_MUZZLEFLASH3:
+ {
+ int iAttachment = -1;
+ Vector attachOrigin;
+ QAngle attachAngles;
+
+ // First person muzzle flashes
+ switch (event)
+ {
+ case CL_EVENT_MUZZLEFLASH0:
+ iAttachment = 0;
+ break;
+
+ case CL_EVENT_MUZZLEFLASH1:
+ iAttachment = 1;
+ break;
+
+ case CL_EVENT_MUZZLEFLASH2:
+ iAttachment = 2;
+ break;
+
+ case CL_EVENT_MUZZLEFLASH3:
+ iAttachment = 3;
+ break;
+ }
+
+ // Did we find it?
+ if ( pViewModel->GetAttachment( iAttachment+1, attachOrigin, attachAngles ) )
+ {
+ //int iType = atoi( options );
+
+ // Is our owner boosted?
+ CBasePlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer && pPlayer->HasPowerup(POWERUP_BOOST) )
+ {
+ unsigned char color[3];
+ color[0] = 50;
+ color[1] = 255;
+ color[2] = 50;
+ FX_MuzzleEffect( attachOrigin, attachAngles, 1.0, GetRefEHandle(), &color[0] );
+ }
+ else
+ {
+ FX_MuzzleEffect( attachOrigin, attachAngles, 1.0, GetRefEHandle() );
+ }
+
+ return true;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+//============================================================================================================
+// OWNER BOOSTED PROXY FOR WEAPONS / PLAYERS
+//============================================================================================================
+class CPlayerBoostedProxy : public CResultProxy
+{
+public:
+ bool Init( IMaterial *pMaterial, KeyValues *pKeyValues );
+ void OnBind( void *pC_BaseEntity );
+
+private:
+ CFloatInput m_Factor;
+};
+
+bool CPlayerBoostedProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
+{
+ if (!CResultProxy::Init( pMaterial, pKeyValues ))
+ return false;
+
+ if (!m_Factor.Init( pMaterial, pKeyValues, "scale", 1 ))
+ return false;
+
+ return true;
+}
+
+void CPlayerBoostedProxy::OnBind( void *pRenderable )
+{
+ // Find the view angle between the player and this entity....
+ IClientRenderable *pRend = (IClientRenderable *)pRenderable;
+ C_BaseEntity *pEntity = pRend->GetIClientUnknown()->GetBaseEntity();
+ CBaseTFPlayer *pPlayer = NULL;
+ C_BaseViewModel *pViewModel = dynamic_cast<C_BaseViewModel*>(pEntity);
+ if ( pViewModel )
+ {
+ pPlayer = C_BaseTFPlayer::GetLocalPlayer();
+ }
+ else
+ {
+ CBaseTFCombatWeapon *pWeapon = dynamic_cast<CBaseTFCombatWeapon*>(pEntity);
+ if ( pWeapon )
+ {
+ pPlayer = ToBaseTFPlayer( pWeapon->GetOwner() );
+ }
+ else
+ {
+ pPlayer = dynamic_cast<CBaseTFPlayer*>(pEntity);
+ }
+ }
+
+ // Find him?
+ if ( pPlayer )
+ {
+ float flBoosted = (int)pPlayer->HasPowerup( POWERUP_BOOST );
+ SetFloatResult( flBoosted * m_Factor.GetFloat() );
+ }
+}
+
+EXPOSE_INTERFACE( CPlayerBoostedProxy, IMaterialProxy, "PlayerBoosted" IMATERIAL_PROXY_INTERFACE_VERSION );
+
+
+#else
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float CBaseTFCombatWeapon::CalcViewmodelBob( void )
+{
+ return 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+void CBaseTFCombatWeapon::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
+{
+}
+
+#endif
+
+LINK_ENTITY_TO_CLASS( basetfcombatweapon, CBaseTFCombatWeapon );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseTFCombatWeapon , DT_BaseTFCombatWeapon )
+
+BEGIN_NETWORK_TABLE( CBaseTFCombatWeapon , DT_BaseTFCombatWeapon )
+#if !defined( CLIENT_DLL )
+
+ // Don't network any animation stuff to client
+ SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ),
+ SendPropExclude( "DT_BaseAnimating", "m_flCycle" ),
+
+ SendPropInt( SENDINFO( m_bReflectViewModelAnimations ), 1, SPROP_UNSIGNED ),
+#else
+ RecvPropInt( RECVINFO( m_bReflectViewModelAnimations ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CBaseTFCombatWeapon )
+
+ // If true, reflect and weapon animations to all view models
+ DEFINE_PRED_FIELD( m_bReflectViewModelAnimations, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_FIELD( m_iLastReflectedActivity, FIELD_INTEGER )
+
+END_PREDICTION_DATA()
diff --git a/game/shared/tf2/basetfcombatweapon_shared.h b/game/shared/tf2/basetfcombatweapon_shared.h
new file mode 100644
index 0000000..4c16acf
--- /dev/null
+++ b/game/shared/tf2/basetfcombatweapon_shared.h
@@ -0,0 +1,152 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BASETFCOMBATWEAPON_SHARED_H
+#define BASETFCOMBATWEAPON_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "baseplayer_shared.h"
+#include "basetfplayer_shared.h"
+#include "basecombatweapon_shared.h"
+
+#if defined( CLIENT_DLL )
+#define CBaseTFCombatWeapon C_BaseTFCombatWeapon
+#endif
+//-----------------------------------------------------------------------------
+// Purpose: Client side rep of CBaseTFCombatWeapon
+//-----------------------------------------------------------------------------
+class CBaseTFCombatWeapon : public CBaseCombatWeapon
+{
+ DECLARE_CLASS( CBaseTFCombatWeapon, CBaseCombatWeapon );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CBaseTFCombatWeapon ();
+
+ virtual void Precache( void );
+
+ bool IsCamouflaged( void );
+
+ virtual Vector GetTracerSrc( Vector &vecSrc, Vector &vecFireDir );
+
+ // Check if the owner is being EMP'd and if we can't fire, play an appropriate
+ // failure sound
+ // Default is to allow firing no matter what
+ virtual bool ComputeEMPFireState( void ) { return true; }
+
+ virtual void CheckRemoveDisguise( void );
+
+ virtual int GetImpactScale( void ) { return 1; };
+
+ // FIXME: why are these virtual?
+ virtual float SequenceDuration( int iSequence );
+ virtual float SequenceDuration( void ) { return SequenceDuration( GetSequence() ); }
+
+ virtual void WeaponSound( WeaponSound_t sound_type, float soundtime = 0.0f );
+
+ virtual void PlayAttackAnimation( int activity );
+
+ virtual bool SendWeaponAnim( int iActivity );
+ virtual void SetReflectViewModelAnimations( bool reflect );
+ virtual bool IsReflectingAnimations( void ) const;
+ virtual int GetLastReflectedActivity( void ) { return m_iLastReflectedActivity; };
+ virtual int GetOtherWeaponsActivity( int iActivity ) { return iActivity; }
+ virtual int ReplaceOtherWeaponsActivity( int iActivity ) { return iActivity; }
+ virtual bool SupportsTwoHanded( void ) { return false; };
+
+ virtual void CleanupOnActStart( void ) { return; }
+
+ bool IsOwnerEMPed();
+
+ virtual void BulletWasFired( const Vector &vecStart, const Vector &vecEnd ) { return; };
+
+ // Technology handling
+ virtual void GainedNewTechnology( CBaseTechnology *pTechnology ) { return; };
+
+ /*
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+ */
+
+ virtual int GetPrimaryAmmo( void );
+
+ virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles );
+ virtual float CalcViewmodelBob( void );
+
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() &&
+ GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ // Camo
+ virtual int GetFxBlend( void );
+ virtual bool IsTransparent( void );
+
+ virtual int GetSecondaryAmmo( void );
+ virtual int DrawModel( int flags );
+ virtual void DrawAmmo( void );
+ virtual void DrawMiniAmmo( void );
+ virtual bool ShouldDrawPickup( void );
+
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+
+ virtual const char *GetPrintName( void );
+ virtual bool ShouldShowUsageHint( void );
+
+ static void CreateCrosshairPanels( void );
+ static void DestroyCrosshairPanels( void );
+
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+
+protected:
+ static vgui::Label *m_pCrosshairAmmo;
+
+private:
+ // Share crosshair stuff among all weapons
+ static bool m_bCrosshairInitialized;
+ // Create/destroy shared crosshair object
+ static void InitializeCrosshairPanels( void );
+
+private:
+ CBaseTFCombatWeapon ( const CBaseTFCombatWeapon & );
+
+#else
+ virtual void AddAssociatedObject( CBaseObject *pObject ) { }
+ virtual void RemoveAssociatedObject( CBaseObject *pObject ) { }
+protected:
+
+ // CVars that contain my damage details
+ const ConVar *m_pDamageCVar;
+ const ConVar *m_pRangeCVar;
+
+#endif
+
+protected:
+ // If true, reflect and weapon animations to all view models
+ CNetworkVar( bool, m_bReflectViewModelAnimations );
+ int m_iLastReflectedActivity;
+
+ float bobtime;
+ float bob;
+};
+
+#endif // BASETFCOMBATWEAPON_SHARED_H
+
+
+
diff --git a/game/shared/tf2/basetfplayer_shared.cpp b/game/shared/tf2/basetfplayer_shared.cpp
new file mode 100644
index 0000000..0634411
--- /dev/null
+++ b/game/shared/tf2/basetfplayer_shared.cpp
@@ -0,0 +1,825 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: TF2's player object, code shared between client & server.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combatshield.h"
+#include "weapon_objectselection.h"
+#include "weapon_twohandedcontainer.h"
+#ifdef CLIENT_DLL
+#include "c_weapon_builder.h"
+#else
+#include "weapon_builder.h"
+#include "basegrenade_shared.h"
+#include "grenade_objectsapper.h"
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseTFPlayer::IsClass( TFClass iClass )
+{
+ if ( !GetPlayerClass() )
+ {
+ // Special case for undecided players
+ if ( iClass == TFCLASS_UNDECIDED )
+ return true;
+ return false;
+ }
+
+ return ( PlayerClass() == iClass );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponCombatShield *CBaseTFPlayer::GetCombatShield( void )
+{
+ if ( !m_hWeaponCombatShield )
+ {
+ if ( GetTeamNumber() == TEAM_ALIENS )
+ {
+ m_hWeaponCombatShield = static_cast< CWeaponCombatShield * >( Weapon_OwnsThisType( "weapon_combat_shield_alien" ) );
+#ifndef CLIENT_DLL
+ if ( !m_hWeaponCombatShield )
+ {
+ m_hWeaponCombatShield = static_cast< CWeaponCombatShield * >( GiveNamedItem( "weapon_combat_shield_alien" ) );
+ }
+#endif
+ }
+ else
+ {
+ m_hWeaponCombatShield = static_cast< CWeaponCombatShield * >( Weapon_OwnsThisType( "weapon_combat_shield" ) );
+#ifndef CLIENT_DLL
+ if ( !m_hWeaponCombatShield )
+ {
+ m_hWeaponCombatShield = static_cast< CWeaponCombatShield * >( GiveNamedItem( "weapon_combat_shield" ) );
+ }
+#endif
+ }
+ }
+
+ return m_hWeaponCombatShield;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check to see if the shot is blocked by the player's handheld shield
+//-----------------------------------------------------------------------------
+bool CBaseTFPlayer::IsHittingShield( const Vector &vecVelocity, float *flDamage )
+{
+ if (!IsParrying() && !IsBlocking())
+ return false;
+
+ Vector2D vecDelta = vecVelocity.AsVector2D();
+ Vector2DNormalize( vecDelta );
+
+ Vector forward;
+ AngleVectors( GetLocalAngles(), &forward );
+
+ Vector2DNormalize( forward.AsVector2D() );
+
+ float flDot = DotProduct2D( vecDelta, forward.AsVector2D() );
+
+ // This gives us a little more than a 90 degree protection angle
+ if (flDot < -0.67f)
+ {
+ // We've hit the players handheld shield, see if the shield can do anything about it
+ if ( flDamage && GetCombatShield() )
+ {
+ // Return true if the shield blocked it all
+ *flDamage = GetCombatShield()->AttemptToBlock( *flDamage );
+ return ( !(*flDamage) );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play a sound to show we've been hurt
+//-----------------------------------------------------------------------------
+void CBaseTFPlayer::PainSound( void )
+{
+ char *sSoundName = NULL;
+
+ if ( GetTeamNumber() == TEAM_HUMANS )
+ {
+ sSoundName = "Humans.Pain";
+ }
+ else if ( GetTeamNumber() == TEAM_ALIENS )
+ {
+ switch( PlayerClass() )
+ {
+ case TFCLASS_COMMANDO:
+ sSoundName = "AlienCommando.Pain";
+ break;
+
+ case TFCLASS_MEDIC:
+ sSoundName = "AlienMedic.Pain";
+ break;
+
+ case TFCLASS_DEFENDER:
+ sSoundName = "AlienDefender.Pain";
+ break;
+
+ case TFCLASS_ESCORT:
+ sSoundName = "AlienEscort.Pain";
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if ( !sSoundName )
+ return;
+
+ CPASAttenuationFilter filter( this, sSoundName );
+ EmitSound( filter, entindex(), sSoundName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if we should record our last weapon when switching between the two specified weapons
+//-----------------------------------------------------------------------------
+bool CBaseTFPlayer::Weapon_ShouldSetLast( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon )
+{
+ // Don't record last weapons when switching to an object
+ if ( dynamic_cast< CWeaponObjectSelection* >( pNewWeapon ) )
+ {
+ // Store this weapon off so we can switch back to it
+ // Don't store it if it's also an object
+ CBaseCombatWeapon *pLast = pOldWeapon->GetLastWeapon();
+#ifdef CLIENT_DLL
+ if ( !dynamic_cast< C_WeaponBuilder* >( pLast ) )
+#else
+ if ( !dynamic_cast< CWeaponBuilder* >( pLast ) )
+#endif
+ {
+ m_hLastWeaponBeforeObject = pLast;
+ }
+ return false;
+ }
+
+ // Don't record last weapons when switching from the builder
+ // If the old weapon is a twohanded container, check the left weapon
+ CWeaponTwoHandedContainer *pContainer = dynamic_cast< CWeaponTwoHandedContainer * >( pOldWeapon );
+ if ( pContainer )
+ {
+ pOldWeapon = dynamic_cast< CBaseTFCombatWeapon * >( pContainer->GetLeftWeapon() );
+ }
+#ifdef CLIENT_DLL
+ if ( dynamic_cast< C_WeaponBuilder* >( pOldWeapon ) )
+ return false;
+#else
+ if ( dynamic_cast< CWeaponBuilder* >( pOldWeapon ) )
+ return false;
+#endif
+
+ return BaseClass::Weapon_ShouldSetLast( pOldWeapon, pNewWeapon );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if we should allow selection of the specified item
+//-----------------------------------------------------------------------------
+bool CBaseTFPlayer::Weapon_ShouldSelectItem( CBaseCombatWeapon *pWeapon )
+{
+ CBaseCombatWeapon *pActiveWeapon = GetActiveWeapon();
+ // If the old weapon is a twohanded container, check the left weapon
+ CWeaponTwoHandedContainer *pContainer = dynamic_cast< CWeaponTwoHandedContainer * >( pActiveWeapon );
+ if ( pContainer )
+ {
+ pActiveWeapon = pContainer->GetLeftWeapon();
+ }
+
+ return ( pWeapon != pActiveWeapon );
+}
+
+#ifndef CLIENT_DLL
+// Sapper handling is all here because it'll soon be shared Client / Server
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseTFPlayer::IsAttachingSapper( void )
+{
+ return ( m_TFLocal.m_bAttachingSapper );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CBaseTFPlayer::GetSapperAttachmentTime( void )
+{
+ return (gpGlobals->curtime - m_flSapperAttachmentStartTime);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFPlayer::StartAttachingSapper( CBaseObject *pObject, CGrenadeObjectSapper *pSapper )
+{
+ Assert( pSapper );
+
+ m_TFLocal.m_bAttachingSapper = true;
+ m_TFLocal.m_flSapperAttachmentFrac = 0.0f;
+
+ m_hSappedObject = pObject;
+ m_flSapperAttachmentStartTime = gpGlobals->curtime;
+ m_flSapperAttachmentFinishTime = gpGlobals->curtime + m_hSappedObject->GetSapperAttachTime();
+ m_hSapper = pSapper;
+ m_hSapper->SetArmed( false );
+
+ CPASAttenuationFilter filter( m_hSapper, "WeaponObjectSapper.Attach" );
+ EmitSound( filter, m_hSapper->entindex(), "WeaponObjectSapper.Attach" );
+
+ // Drop the player's weapon
+ if ( GetActiveWeapon() )
+ {
+ GetActiveWeapon()->Holster();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFPlayer::CheckSapperAttaching( void )
+{
+ // Did we stop attaching?
+ if ( !m_TFLocal.m_bAttachingSapper )
+ {
+ if ( m_TFLocal.m_flSapperAttachmentFrac )
+ {
+ StopAttaching();
+ }
+ return;
+ }
+
+ // Object gone?
+ if ( m_hSappedObject == NULL )
+ {
+ StopAttaching();
+ return;
+ }
+
+ // Sapper gone?
+ if ( m_hSapper == NULL )
+ {
+ StopAttaching();
+ return;
+ }
+
+ // Make sure I'm still looking at the target
+ trace_t tr;
+ Vector vecAiming;
+ Vector vecSrc = EyePosition();
+ EyeVectors( &vecAiming );
+ UTIL_TraceLine( vecSrc, vecSrc + (vecAiming * 128), MASK_SOLID, this, TFCOLLISION_GROUP_WEAPON, &tr );
+ if ( tr.fraction == 1.0 || tr.m_pEnt != m_hSappedObject )
+ {
+ StopAttaching();
+ return;
+ }
+
+ // Finished?
+ if ( m_flSapperAttachmentFinishTime >= gpGlobals->curtime )
+ {
+ float dt = m_flSapperAttachmentFinishTime - m_flSapperAttachmentStartTime;
+ if ( dt > 0.0f )
+ {
+ m_TFLocal.m_flSapperAttachmentFrac = ( gpGlobals->curtime - m_flSapperAttachmentStartTime ) / dt;
+ m_TFLocal.m_flSapperAttachmentFrac = clamp( m_TFLocal.m_flSapperAttachmentFrac, 0.0f, 1.0f );
+ }
+ else
+ {
+ m_TFLocal.m_flSapperAttachmentFrac = 0.0f;
+ }
+ return;
+ }
+
+ FinishAttaching();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFPlayer::CleanupAfterAttaching( void )
+{
+ Assert( m_TFLocal.m_bAttachingSapper );
+ m_TFLocal.m_bAttachingSapper = false;
+
+ m_flSapperAttachmentFinishTime = -1;
+ m_flSapperAttachmentStartTime = -1;
+ m_TFLocal.m_flSapperAttachmentFrac = 0.0f;
+
+ // Restore the player's weapon
+ m_flNextAttack = gpGlobals->curtime;
+ if ( GetActiveWeapon() )
+ {
+ GetActiveWeapon()->Deploy();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFPlayer::StopAttaching( void )
+{
+ CleanupAfterAttaching();
+
+ if ( m_hSapper != NULL )
+ {
+ CPASAttenuationFilter filter( m_hSapper, "WeaponObjectSapper.AttachFail" );
+ EmitSound( filter, m_hSapper->entindex(), "WeaponObjectSapper.AttachFail" );
+
+ m_hSapper->SetTargetObject( NULL );
+ m_hSapper->Remove( );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFPlayer::FinishAttaching( void )
+{
+ CleanupAfterAttaching();
+
+ if ( m_hSapper != NULL )
+ {
+ m_hSapper->SetTargetObject( m_hSappedObject );
+ m_hSapper->SetArmed( true );
+ }
+}
+#endif
+
+// 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( CBaseTFPlayer *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;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerAnimState::Update()
+{
+ m_angRender = GetOuter()->GetLocalAngles();
+
+ ComputePoseParam_BodyYaw();
+ ComputePoseParam_BodyPitch( GetOuter()->GetModelPtr() );
+ ComputePoseParam_BodyLookYaw();
+
+ ComputePlaybackRate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CPlayerAnimState::ComputePlaybackRate()
+{
+ // Determine ideal playback rate
+ Vector vel;
+ GetOuterAbsVelocity( vel );
+
+ float speed = vel.Length2D();
+
+ bool isMoving = ( speed > 0.5f ) ? true : false;
+
+ Activity currentActivity = GetOuter()->GetSequenceActivity( GetOuter()->GetSequence() );
+
+ switch ( currentActivity )
+ {
+ case ACT_WALK:
+ case ACT_RUN:
+ case ACT_IDLE:
+ {
+ float maxspeed = GetOuter()->MaxSpeed();
+ if ( isMoving && ( maxspeed > 0.0f ) )
+ {
+ float flFactor = 1.0f;
+
+ // HACK HACK:: Defender backward animation is animated at 0.6 times speed, so scale up animation for this class
+ // if he's running backward.
+
+ // Not sure if we're really going to do all classes this way.
+ if ( GetOuter()->IsClass( TFCLASS_DEFENDER ) ||
+ GetOuter()->IsClass( TFCLASS_MEDIC ) )
+ {
+ Vector facing;
+ Vector moving;
+
+ moving = vel;
+ AngleVectors( GetOuter()->GetLocalAngles(), &facing );
+ VectorNormalize( moving );
+
+ float dot = moving.Dot( facing );
+ if ( dot < 0.0f )
+ {
+ float backspeed = sv_backspeed.GetFloat();
+ flFactor = 1.0f - fabs( dot ) * (1.0f - backspeed);
+
+ if ( flFactor > 0.0f )
+ {
+ flFactor = 1.0f / flFactor;
+ }
+ }
+ }
+
+ // 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 );
+ }
+ }
+ break;
+ default:
+ {
+ GetOuter()->SetPlaybackRate( 1.0f );
+ }
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CBasePlayer
+//-----------------------------------------------------------------------------
+CBaseTFPlayer *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 );
+}
+
+//-----------------------------------------------------------------------------
+// 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;
+
+ // See if we have a blender for pitch
+ int pitch = GetOuter()->LookupPoseParameter( pStudioHdr, "body_pitch" );
+ if ( pitch < 0 )
+ return;
+
+ GetOuter()->SetPoseParameter( pStudioHdr, 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;
+
+ // See if we even have a blender for pitch
+ int upper_body_yaw = GetOuter()->LookupPoseParameter( "body_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 = mp_feetyawrate.GetFloat();
+
+ Vector vel;
+
+ GetOuterAbsVelocity( vel );
+
+ bool isMoving = ( vel.Length() > 0.0f ) ? true : false;
+
+ if ( !isMoving )
+ {
+ // Just stopped moving, try and clamp feet
+ if ( m_flLastTurnTime <= 0.0f )
+ {
+ m_flLastTurnTime = gpGlobals->curtime;
+ m_flLastYaw = GetOuter()->GetAbsAngles().y;
+ // Snap feet to be perfectly aligned with torso/eyes
+ m_flGoalFeetYaw = GetOuter()->GetAbsAngles().y;
+ m_flCurrentFeetYaw = m_flGoalFeetYaw;
+ m_nTurningInPlace = TURN_NONE;
+ }
+
+ // If rotating in place, update stasis timer
+ if ( m_flLastYaw != GetOuter()->GetAbsAngles().y )
+ {
+ m_flLastTurnTime = gpGlobals->curtime;
+ m_flLastYaw = GetOuter()->GetAbsAngles().y;
+ }
+
+ if ( m_flGoalFeetYaw != m_flCurrentFeetYaw )
+ {
+ m_flLastTurnTime = gpGlobals->curtime;
+ }
+
+ turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
+
+ // See how far off current feetyaw is from true yaw
+ float yawdelta = GetOuter()->GetAbsAngles().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 > MAX_TORSO_ANGLE )
+ {
+ 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()->GetAbsAngles().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()->GetAbsAngles().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_flGoalFeetYaw = GetOuter()->GetAbsAngles().y;
+ flGoalTorsoYaw = 0.0f;
+ turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
+ m_flCurrentTorsoYaw = GetOuter()->GetAbsAngles().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;
+ }
+ }
+
+ // Counter rotate upper body as needed
+ ConvergeAngles( flGoalTorsoYaw, turnrate, gpGlobals->frametime, m_flCurrentTorsoYaw );
+
+ // Rotate entire body into position
+ absangles = GetOuter()->GetAbsAngles();
+ absangles.y = m_flCurrentFeetYaw;
+ m_angRender = absangles;
+
+ GetOuter()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -90.0f, 90.0f ) );
+}
+
+
+
+//-----------------------------------------------------------------------------
+// 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
+}
diff --git a/game/shared/tf2/basetfplayer_shared.h b/game/shared/tf2/basetfplayer_shared.h
new file mode 100644
index 0000000..0085f01
--- /dev/null
+++ b/game/shared/tf2/basetfplayer_shared.h
@@ -0,0 +1,22 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BASETFPLAYER_SHARED_H
+#define BASETFPLAYER_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+// Shared header file for players
+#if defined( CLIENT_DLL )
+#define CBaseTFPlayer C_BaseTFPlayer
+#include "c_basetfplayer.h"
+#else
+#include "tf_player.h"
+#endif
+
+#endif // BASETFPLAYER_SHARED_H
diff --git a/game/shared/tf2/basetfvehicle.cpp b/game/shared/tf2/basetfvehicle.cpp
new file mode 100644
index 0000000..1825753
--- /dev/null
+++ b/game/shared/tf2/basetfvehicle.cpp
@@ -0,0 +1,1179 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A base vehicle class
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "basetfvehicle.h"
+#include "tf_movedata.h"
+#include "in_buttons.h"
+#include "baseplayer_shared.h"
+
+#if defined( CLIENT_DLL )
+ #include "hud_vehicle_role.h"
+ #include "hud.h"
+ #include "hud_crosshair.h"
+#else
+ #include "tf_team.h"
+ #include "tf_gamerules.h"
+ #include "tf_func_construction_yard.h"
+ #include "ndebugoverlay.h"
+#endif
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseTFVehicle, DT_BaseTFVehicle );
+
+BEGIN_NETWORK_TABLE( CBaseTFVehicle, DT_BaseTFVehicle )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO(m_nMaxPassengers), CBaseTFVehicle::MAX_PASSENGER_BITS, SPROP_UNSIGNED ),
+ SendPropArray( SendPropEHandle(SENDINFO_ARRAY(m_hPassengers)), m_hPassengers ),
+ SendPropEHandle( SENDINFO(m_hDriverGun) ),
+#else
+ RecvPropInt( RECVINFO(m_nMaxPassengers) ),
+ RecvPropArray( RecvPropEHandle(RECVINFO(m_hPassengers[0])), m_hPassengers ),
+ RecvPropEHandle( RECVINFO(m_hDriverGun) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CBaseTFVehicle )
+
+ DEFINE_PRED_ARRAY( m_hPassengers, FIELD_EHANDLE, CBaseTFVehicle::MAX_PASSENGERS, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+extern float RemapAngleRange( float startInterval, float endInterval, float value );
+extern ConVar road_feel;
+
+ConVar vehicle_view_offset_forward( "vehicle_view_offset_forward", "-280", FCVAR_REPLICATED );
+ConVar vehicle_view_offset_right( "vehicle_view_offset_right", "0", FCVAR_REPLICATED );
+ConVar vehicle_view_offset_up( "vehicle_view_offset_up", "50", FCVAR_REPLICATED );
+ConVar vehicle_thirdperson( "vehicle_thirdperson", "1", FCVAR_REPLICATED, "Enable/disable thirdperson camera view in vehicles" );
+
+ConVar vehicle_attach_eye_angles( "vehicle_attach_eye_angles", "0", FCVAR_REPLICATED, "Attach player eye angles to vehicle attachments" );
+
+#define PITCH_CURVE_ZERO 10 // pitch less than this is clamped to zero
+#define PITCH_CURVE_LINEAR 45 // pitch greater than this is copied out
+
+#define ROLL_CURVE_ZERO 5 // roll less than this is clamped to zero
+#define ROLL_CURVE_LINEAR 45 // roll greater than this is copied out
+
+#if defined( CLIENT_DLL )
+ ConVar road_feel( "road_feel", "0.1", FCVAR_NOTIFY | FCVAR_REPLICATED );
+#else
+ // Deterioration
+ #define DETERIORATION_THINK_CONTEXT "VehicleDeteriorationThink"
+ #define PASSENGER_THINK_CONTEXT "VehiclePassengerThink"
+ ConVar vehicle_deterioration_start_time( "vehicle_deterioration_start_time", "90", 0, "Time it takes for a vehicle to start deteriorating after being left alone." );
+
+ #define DETERIORATION_DISTANCE (600 * 600) // Never deteriorate if team mates are within this distance
+
+#endif // CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseTFVehicle::CBaseTFVehicle()
+{
+ SetPredictionEligible( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::Spawn()
+{
+ BaseClass::Spawn();
+ CollisionProp()->SetSurroundingBoundsType( USE_OBB_COLLISION_BOUNDS );
+
+#if defined( CLIENT_DLL )
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ m_pIconDefaultCrosshair = NULL;
+#else
+ m_fObjectFlags |= OF_DOESNT_NEED_POWER | OF_MUST_BE_BUILT_ON_ATTACHMENT;
+ SetContextThink( VehiclePassengerThink, 2.0, PASSENGER_THINK_CONTEXT );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Vehicle overrides
+//-----------------------------------------------------------------------------
+CBaseEntity* CBaseTFVehicle::GetVehicleEnt()
+{
+ return this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
+{
+ // animate + update attachment points
+#ifdef CLIENT_DLL
+ StudioFrameAdvance();
+#else
+ StudioFrameAdvance();
+ // This calls StudioFrameAdvance, then we use the results from that to determine where to move.
+ DispatchAnimEvents( this );
+#endif
+
+ CTFMoveData *pMoveData = (CTFMoveData*)move;
+ Assert( sizeof(VehicleBaseMoveData_t) <= pMoveData->VehicleDataMaxSize() );
+
+ VehicleBaseMoveData_t *pVehicleData = (VehicleBaseMoveData_t*)pMoveData->VehicleData();
+ pVehicleData->m_pVehicle = this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
+{
+ VehicleDriverGunThink();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the driver as a tfplayer pointer
+//-----------------------------------------------------------------------------
+CBaseTFPlayer *CBaseTFVehicle::GetDriverPlayer()
+{
+ return m_hPassengers[VEHICLE_DRIVER].Get();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Can we get into the vehicle?
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::CanGetInVehicle( CBaseTFPlayer *pPlayer )
+{
+ if ( !IsPowered() )
+ return false;
+
+ if ( !InSameTeam( pPlayer ) )
+ return false;
+
+ // Player/Class-specific query.
+ return pPlayer->CanGetInVehicle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Here's where we deal with weapons
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::OnItemPostFrame( CBaseTFPlayer *pDriver )
+{
+ // If we have a gun for the driver, handle it
+ if ( m_hDriverGun )
+ {
+ if ( GetPassengerRole(pDriver) != VEHICLE_DRIVER )
+ return;
+
+ if ( pDriver->m_nButtons & (IN_ATTACK | IN_ATTACK2) )
+ {
+ // Time to fire?
+ if ( m_hDriverGun->CanFireNow() )
+ {
+ m_hDriverGun->Fire( pDriver );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTFVehicle::GetPassengerRole( CBasePlayer *pEnt )
+{
+ Assert( pEnt->IsPlayer() );
+ for ( int i = m_nMaxPassengers; --i >= 0; )
+ {
+ if (m_hPassengers[i] == pEnt)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+Vector CBaseTFVehicle::GetSoundEmissionOrigin() const
+{
+ return WorldSpaceCenter() + Vector( 0, 0, 64 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBasePlayer* CBaseTFVehicle::GetPassenger( int nRole )
+{
+ return m_hPassengers[nRole].Get();
+}
+
+
+//-----------------------------------------------------------------------------
+// Is a particular player in the vehicle?
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::IsPlayerInVehicle( CBaseTFPlayer *pPlayer )
+{
+ return (GetPassengerRole( pPlayer ) >= 0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTFVehicle::GetPassengerCount() const
+{
+ // FIXME: Cache this off!
+ int nCount = 0;
+ for (int i = m_nMaxPassengers; --i >= 0; )
+ {
+ if (m_hPassengers[i].Get())
+ {
+ ++nCount;
+ }
+ }
+ return nCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTFVehicle::GetMaxPassengerCount() const
+{
+ return m_nMaxPassengers;
+}
+
+//-----------------------------------------------------------------------------
+// Process input
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::ItemPostFrame( CBasePlayer *pPassenger )
+{
+#ifndef CLIENT_DLL
+ Assert( GetPassengerRole( pPassenger ) != -1 );
+ if (pPassenger->m_afButtonPressed & (IN_USE /*| IN_JUMP*/))
+ {
+ // Get the player out..
+ pPassenger->LeaveVehicle();
+ return;
+ }
+#endif
+
+ OnItemPostFrame( static_cast<CBaseTFPlayer*>(pPassenger) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Reset the time before this vehicle begins to deteriorate
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::ResetDeteriorationTime( void )
+{
+#if !defined (CLIENT_DLL)
+ SetContextThink( VehicleDeteriorationThink, gpGlobals->curtime + vehicle_deterioration_start_time.GetFloat(), DETERIORATION_THINK_CONTEXT );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Prevent driving in construction yards
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::IsReadyToDrive( void )
+{
+#if !defined( CLIENT_DLL )
+ return ( PointInConstructionYard( GetAbsOrigin() ) == false );
+#else
+ return true;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Process input
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::SetMaxPassengerCount( int nCount )
+{
+#if !defined( CLIENT_DLL )
+ Assert( (nCount >= 1) && (nCount <= MAX_PASSENGERS) );
+ m_nMaxPassengers = nCount;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+//
+// Server-only code here
+//
+//-----------------------------------------------------------------------------
+
+#if !defined (CLIENT_DLL)
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::FinishedBuilding( void )
+{
+ BaseClass::FinishedBuilding();
+
+ // See if we've finished building on a vehicle that has a passenger slot assigned to my buildpoint.
+ CBaseObject *pParent = GetParentObject();
+ if ( pParent && pParent->IsAVehicle() )
+ {
+ CBaseTFVehicle *pVehicle = static_cast<CBaseTFVehicle*>(pParent);
+ int iRole = pVehicle->GetChildVehicleRole( this );
+ if ( iRole != -1 )
+ {
+ // Is there a player in the role assigned to this buildpoint?
+ CBaseTFPlayer *pExistingPlayer = static_cast<CBaseTFPlayer*>( pVehicle->GetPassenger( iRole ) );
+ if ( pExistingPlayer )
+ {
+ // Remove the player from my parent vehicle and put them in me
+ pExistingPlayer->LeaveVehicle();
+
+ // Get in the vehicle.
+ pExistingPlayer->GetInVehicle( this, VEHICLE_DRIVER );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::VehicleDeteriorationThink( void )
+{
+ StartDeteriorating();
+
+ SetContextThink( NULL, gpGlobals->curtime + vehicle_deterioration_start_time.GetFloat(), DETERIORATION_THINK_CONTEXT );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::VehiclePassengerThink( void )
+{
+ SetNextThink( gpGlobals->curtime + 10.0, PASSENGER_THINK_CONTEXT );
+
+ if ( IsPlacing() )
+ {
+ ResetDeteriorationTime();
+ return;
+ }
+
+ // If there are any passengers in the vehicle, push off deterioration time
+ if ( GetPassengerCount() )
+ {
+ ResetDeteriorationTime();
+ return;
+ }
+
+ // See if there are any team members nearby
+ if ( GetTeam() )
+ {
+ int iNumPlayers = GetTFTeam()->GetNumPlayers();
+ for ( int i = 0; i < iNumPlayers; i++ )
+ {
+ if ( GetTFTeam()->GetPlayer(i) )
+ {
+ Vector vecOrigin = GetTFTeam()->GetPlayer(i)->GetAbsOrigin();
+ if ( (vecOrigin - GetAbsOrigin()).LengthSqr() < DETERIORATION_DISTANCE )
+ {
+ // Found one nearby, reset our deterioration time
+ ResetDeteriorationTime();
+ return;
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Figure out which role of a vehicle a child vehicle is sitting in..
+//-----------------------------------------------------------------------------
+int CBaseTFVehicle::GetChildVehicleRole( CBaseTFVehicle *pChild )
+{
+ int nBuildPoints = GetNumBuildPoints();
+ for( int i = 0; i < nBuildPoints; i++ )
+ {
+ CBaseObject* pObject = GetBuildPointObject(i);
+ if (pObject == pChild)
+ {
+ return GetBuildPointPassenger(i);
+ }
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Vehicles are permanently oriented off angle for vphysics.
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const
+{
+ // This call is necessary to cause m_rgflCoordinateFrame to be recomputed
+ const matrix3x4_t &entityToWorld = EntityToWorldTransform();
+
+ if (pForward != NULL)
+ {
+ MatrixGetColumn( entityToWorld, 1, *pForward );
+ }
+
+ if (pRight != NULL)
+ {
+ MatrixGetColumn( entityToWorld, 0, *pRight );
+ }
+
+ if (pUp != NULL)
+ {
+ MatrixGetColumn( entityToWorld, 2, *pUp );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get into the vehicle
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ BaseClass::Use( pActivator, pCaller, useType, value );
+
+ if ( useType == USE_ON )
+ {
+ CBaseTFPlayer *pPlayer = dynamic_cast<CBaseTFPlayer*>(pActivator);
+ if ( pPlayer && InSameTeam(pPlayer) )
+ {
+ // Check to see if we are really using nearby build points:
+ if( !UseAttachedItem( pActivator, pCaller, useType, value ) )
+ {
+ // Attempt to board the vehicle:
+ AttemptToBoardVehicle( pPlayer );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Figure out if I should be using an attached item rather than this vehicle itself.
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::UseAttachedItem( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ CBaseTFPlayer* pPlayer = dynamic_cast<CBaseTFPlayer*>(pActivator);
+ if ( !pPlayer || !InSameTeam(pPlayer) )
+ return false;
+
+ Vector vecPlayerOrigin = pPlayer->GetAbsOrigin();
+ int nBestBuildPoint = -1;
+ float fBestDistance = FLT_MAX;
+
+ // Get the closest regular entry point:
+ int nRole = LocateEntryPoint( pPlayer, &fBestDistance );
+
+ // Iterate through each of the build points, if any, and see which we are closest to.
+ int nBuildPoints = GetNumBuildPoints();
+ for( int i = 0; i < nBuildPoints; i++ )
+ {
+ CBaseObject* pObject = GetBuildPointObject(i);
+
+ // If there's something in the build point that isn't in the process of being built or placed:
+ if( pObject && !pObject->IsPlacing() && !pObject->IsBuilding() )
+ {
+ Vector vecOrigin;
+ QAngle vecAngles;
+
+ // If the build point is the default point for this role, just take it
+ if (GetBuildPointPassenger(i) == nRole)
+ {
+ nBestBuildPoint = i;
+ break;
+ }
+
+ // And I can get the build point.
+ if( GetBuildPoint( i, vecOrigin, vecAngles ) )
+ {
+ float fLength2dSqr = (vecOrigin - vecPlayerOrigin).AsVector2D().LengthSqr();
+ if( fLength2dSqr < fBestDistance )
+ {
+ nBestBuildPoint = i;
+ fBestDistance = fLength2dSqr;
+ }
+ }
+ }
+ }
+
+ if( nBestBuildPoint >= 0 )
+ {
+ // They're using an item on me, so push out the deterioration time
+ ResetDeteriorationTime();
+ GetBuildPointObject(nBestBuildPoint)->Use( pActivator, pCaller, useType, value );
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Object has been removed...
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::DestroyObject( void )
+{
+ for (int i = m_nMaxPassengers; --i >= 0; )
+ {
+ if (m_hPassengers[i])
+ {
+ m_hPassengers[i]->LeaveVehicle();
+ }
+ }
+
+ BaseClass::DestroyObject();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTFVehicle::GetEmptyRole( void )
+{
+ for ( int iPassenger = 0; iPassenger < m_nMaxPassengers; ++iPassenger )
+ {
+ if ( !m_hPassengers[iPassenger].Get() )
+ return iPassenger;
+ }
+
+ return -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Try to board the vehicle
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::AttemptToBoardVehicle( CBaseTFPlayer *pPlayer )
+{
+ if ( !CanGetInVehicle( pPlayer ) )
+ return;
+
+ // Locate the entry point.
+ int nRole = LocateEntryPoint( pPlayer );
+ if ( nRole != -1 )
+ {
+ // Set the owner flag.
+ bool bOwner = ( pPlayer == GetOwner() );
+ if ( bOwner )
+ {
+ // Check to see if a player exists at this location (role).
+ CBaseTFPlayer *pExistingPlayer = static_cast<CBaseTFPlayer*>( GetPassenger( nRole ) );
+ if ( pExistingPlayer )
+ {
+ pExistingPlayer->LeaveVehicle();
+
+ // Get in the vehicle.
+ pPlayer->GetInVehicle( this, nRole );
+
+ // Then see if we can move the other player to another slot in this vehicle
+ int nEmptyRole = GetEmptyRole();
+ if ( nEmptyRole != -1 )
+ {
+ pExistingPlayer->GetInVehicle( this, nEmptyRole );
+ }
+ return;
+ }
+ }
+
+ // Get in the vehicle.
+ pPlayer->GetInVehicle( this, nRole );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle commands sent from vgui panels on the client
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::ClientCommand( CBaseTFPlayer *pPlayer, const CCommand &args )
+{
+ ResetDeteriorationTime();
+
+ if ( FStrEq( pCmd, "toggle_use" ) )
+ {
+ AttemptToBoardVehicle( pPlayer );
+ return true;
+ }
+
+ return BaseClass::ClientCommand( pPlayer, args );
+}
+
+//-----------------------------------------------------------------------------
+// Get a position in *world space* inside the vehicle for the player to exit at
+// NOTE: This doesn't check for obstructions
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::GetInitialPassengerExitPoint( int nRole, Vector *pAbsPoint, QAngle *pAbsAngles )
+{
+ char pAttachmentName[32];
+ Q_snprintf( pAttachmentName, sizeof( pAttachmentName ), "vehicle_exit_passenger%d", nRole );
+ int exitAttachmentIndex = LookupAttachment(pAttachmentName);
+ if (exitAttachmentIndex <= 0)
+ {
+ // bad attachment, just return the origin
+ *pAbsPoint = GetAbsOrigin();
+ pAbsPoint->z += WorldAlignMaxs()[2] + 50.0f;
+ return;
+ }
+
+ QAngle vehicleExitAngles;
+ if( !pAbsAngles )
+ {
+ pAbsAngles = &vehicleExitAngles;
+ }
+
+ GetAttachment( exitAttachmentIndex, *pAbsPoint, *pAbsAngles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Get a point to leave the vehicle from
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::IsValidExitPoint( int nRole, Vector *pExitPoint, QAngle *pAngles )
+{
+ GetInitialPassengerExitPoint( nRole, pExitPoint, pAngles );
+
+ // Check the exit point:
+ Vector vecStart = *pExitPoint;
+ Vector vecEnd = *pExitPoint + Vector(0,0,20);
+ trace_t tr;
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+ if ( (tr.fraction < 1.f) )
+ return false;
+
+ vecEnd = *pExitPoint + Vector(20,20,20);
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+ if ( (tr.fraction < 1.f) )
+ return false;
+
+ vecEnd = *pExitPoint + Vector(-20,-20,20);
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+ if ( (tr.fraction < 1.f) )
+ return false;
+
+ vecEnd = *pExitPoint + Vector(20,-20,20);
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+ if ( (tr.fraction < 1.f) )
+ return false;
+
+ vecEnd = *pExitPoint + Vector(-20,20,20);
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr);
+ if ( (tr.fraction < 1.f) )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::GetPassengerExitPoint( CBasePlayer *pPlayer, int nRole, Vector *pAbsPosition, QAngle *pAbsAngles )
+{
+
+ // Deal with vehicles built on other vehicles
+ CBaseTFVehicle *pParentVehicle = dynamic_cast<CBaseTFVehicle*>(GetMoveParent());
+ if (pParentVehicle)
+ {
+ int nParentVehicleRole = pParentVehicle->GetChildVehicleRole( this );
+ if (nParentVehicleRole >= 0)
+ {
+ pParentVehicle->GetPassengerExitPoint( pPlayer, nParentVehicleRole, pAbsPosition, pAbsAngles );
+ return;
+ }
+ }
+
+ // Deal with vehicles build on objects
+ IHasBuildPoints *pMount = dynamic_cast<IHasBuildPoints*>(GetMoveParent());
+ if (pMount)
+ {
+ int nBuildPoint = pMount->FindObjectOnBuildPoint( this );
+ if (nBuildPoint >= 0)
+ {
+ pMount->GetExitPoint( pPlayer, nBuildPoint, pAbsPosition, pAbsAngles );
+ return;
+ }
+ }
+
+ Vector vNewPos;
+ GetInitialPassengerExitPoint( nRole, pAbsPosition, pAbsAngles );
+ if ( EntityPlacementTest(pPlayer, *pAbsPosition, vNewPos, true) )
+ {
+ *pAbsPosition = vNewPos;
+ return;
+ }
+
+ // Find the first valid exit point
+ for( int iExitPoint = 0; iExitPoint < m_nMaxPassengers; ++iExitPoint )
+ {
+ if (iExitPoint == nRole)
+ continue;
+
+ GetInitialPassengerExitPoint( iExitPoint, pAbsPosition, pAbsAngles );
+ if ( EntityPlacementTest(pPlayer, *pAbsPosition, vNewPos, true) )
+ {
+ *pAbsPosition = vNewPos;
+ return;
+ }
+ }
+
+ // Worst case, we will be returning the vehicle's origin + 50z here
+ *pAbsPosition = GetAbsOrigin();
+ pAbsPosition->z = WorldAlignMaxs()[2] + 150.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::GetPassengerExitPoint( int nRole, Vector *pAbsPosition, QAngle *pAbsAngles )
+{
+ // FIXME: Clean this up
+ CBasePlayer *pPlayer = GetPassenger(nRole);
+ GetPassengerExitPoint( pPlayer, nRole, pAbsPosition, pAbsAngles );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTFVehicle::GetEntryAnimForPoint( const Vector &vecPoint )
+{
+ return ACTIVITY_NOT_AVAILABLE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTFVehicle::GetExitAnimToUse( Vector &vecEyeExitEndpoint, bool &bAllPointsBlocked )
+{
+ bAllPointsBlocked = false;
+ return ACTIVITY_NOT_AVAILABLE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::HandleEntryExitFinish( bool bExitAnimOn, bool bResetAnim )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+// false -
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::HandlePassengerEntry( CBasePlayer *pPlayer, bool bAllowEntryOutsideZone )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPlayer -
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::HandlePassengerExit( CBasePlayer *pPlayer )
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get and set the current driver.
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::SetPassenger( int nRole, CBasePlayer *pEnt )
+{
+ Assert( !pEnt || pEnt->IsPlayer() );
+ Assert( nRole >= 0 && nRole < m_nMaxPassengers );
+ Assert( !m_hPassengers[nRole].Get() || !pEnt );
+ m_hPassengers.Set( nRole, dynamic_cast<CBaseTFPlayer*>(pEnt) );
+
+ // If the vehicle's deteriorating, I get to own it now
+ if ( IsDeteriorating() )
+ {
+ StopDeteriorating();
+ SetBuilder( (CBaseTFPlayer*)pEnt, true );
+ }
+
+ ResetDeteriorationTime();
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Get a position in *world space* inside the vehicle for the player to start at
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::GetPassengerStartPoint( int nRole, Vector *pAbsPoint, QAngle *pAbsAngles )
+{
+ char pAttachmentName[32];
+ Q_snprintf( pAttachmentName, sizeof( pAttachmentName ), "vehicle_feet_passenger%d", nRole );
+ int nFeetAttachmentIndex = LookupAttachment(pAttachmentName);
+ GetAttachment( nFeetAttachmentIndex, *pAbsPoint, *pAbsAngles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+#define INITIAL_MAX_DISTANCE 999999.0f
+
+int CBaseTFVehicle::LocateEntryPoint( CBaseTFPlayer *pPlayer, float* fBest2dDistanceSqr )
+{
+ // Get the players origin and compare it to all the entry points on the
+ // vehicle.
+ Vector vecPlayerPos = pPlayer->GetAbsOrigin();
+ Vector vecEntryPos;
+ QAngle vecEntryAngle;
+
+ int iMinEntry = -1;
+ float flMinDistance2 = INITIAL_MAX_DISTANCE;
+
+ // Is the player the owner of the vehicle?
+ bool bOwner = ( pPlayer == GetOwner() );
+
+ char szPassengerEyes[32];
+ for( int iEntryPoint = 0; iEntryPoint < m_nMaxPassengers; ++iEntryPoint )
+ {
+ // If not the owner, check to see if the entry point is available. The
+ // entry point is always available for the owner.
+ bool bOccupied = ( GetPassenger( iEntryPoint ) != NULL );
+
+ // Also check for child vehicles...
+
+ if ( bOccupied && !bOwner )
+ continue;
+
+ // FIXME: Cache off the entry point
+ Q_snprintf( szPassengerEyes, sizeof( szPassengerEyes ), "vehicle_feet_passenger%d", iEntryPoint );
+ int nAttachmentIndex = LookupAttachment( szPassengerEyes );
+
+ float flDistance2;
+ if (nAttachmentIndex > 0)
+ {
+ GetAttachment( nAttachmentIndex, vecEntryPos, vecEntryAngle );
+ Vector vecDelta = vecEntryPos - vecPlayerPos;
+ flDistance2 = vecDelta.AsVector2D().LengthSqr();
+ }
+ else
+ {
+ // No attachment? Choose it if we must as a last resort
+ flDistance2 = INITIAL_MAX_DISTANCE - 1;
+ }
+
+ if ( flDistance2 < flMinDistance2 )
+ {
+ flMinDistance2 = flDistance2;
+ iMinEntry = iEntryPoint;
+ }
+ }
+
+ if( fBest2dDistanceSqr )
+ {
+ *fBest2dDistanceSqr = flMinDistance2;
+ }
+ return iMinEntry;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a gun that the driver can control
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::SetDriverGun( CBaseObjectDriverGun *pGun )
+{
+ m_hDriverGun = pGun;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the driver's gun
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::VehicleDriverGunThink( void )
+{
+ if ( !m_hDriverGun )
+ return;
+
+ // No driver?
+ CBaseTFPlayer *pDriver = GetDriverPlayer();
+ if ( !pDriver )
+ return;
+
+ QAngle vecTargetAngles = m_hDriverGun->GetCurrentAngles();
+
+ // Cast a ray out of the view to see where the player is looking.
+ trace_t trace;
+ Vector vecForward;
+ Vector vecSrc;
+ QAngle angEyeAngles;
+ GetVehicleViewPosition( VEHICLE_DRIVER, &vecSrc, &angEyeAngles, NULL );
+ AngleVectors( angEyeAngles, &vecForward, NULL, NULL );
+ Vector vecEnd = vecSrc + (vecForward * 10000);
+ UTIL_TraceLine( vecSrc, vecEnd, MASK_OPAQUE, this, COLLISION_GROUP_NONE, &trace );
+
+ //NDebugOverlay::Box( vecSrc, -Vector(10,10,10), Vector(10,10,10), 255,0,0,8, 5 );
+ //NDebugOverlay::Box( vecEnd, -Vector(10,10,10), Vector(10,10,10), 0,255,0,8, 5 );
+ //NDebugOverlay::Box( trace.endpos, -Vector(10,10,10), Vector(10,10,10), 255,255,255,8, 0.1 );
+
+ if ( trace.fraction < 1 )
+ {
+ // Figure out what angles our turret needs to be at in order to hit the target.
+ Vector vFireOrigin = m_hDriverGun->GetFireOrigin();
+
+ //NDebugOverlay::Box( vFireOrigin, -Vector(10,10,10), Vector(10,10,10), 0,255,0,8, 0.1 );
+
+ // Get a direction vector that points at the target.
+ Vector vTo = trace.endpos - vFireOrigin;
+
+ // Transform it into the tank's local space.
+ matrix3x4_t tankToWorld;
+ AngleMatrix( GetAbsAngles(), tankToWorld );
+
+ Vector vLocalTo;
+ VectorITransform( vTo, tankToWorld, vLocalTo );
+
+ // Now figure out what the angles are in local space.
+ QAngle localAngles;
+ VectorAngles( vLocalTo, localAngles );
+
+ vecTargetAngles[YAW] = localAngles[YAW] - 90;
+ vecTargetAngles[PITCH] = anglemod( localAngles[PITCH] );
+ }
+
+ // Set the gun's angles
+ m_hDriverGun->SetTargetAngles( vecTargetAngles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::ShouldUseThirdPersonVehicleView()
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the unperterbed view position for a particular role
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::GetRoleViewPosition( int nRole, Vector *pVehicleEyeOrigin, QAngle *pVehicleEyeAngles )
+{
+ // Generate the view position in world space.
+ Vector vAbsOrigin;
+ QAngle vAbsAngle;
+ bool bUsingThirdPersonCamera = GetRoleAbsViewPosition( nRole, &vAbsOrigin, &vAbsAngle );
+
+
+ // Make a matrix for it.
+ matrix3x4_t absMatrix;
+ AngleMatrix( vAbsAngle, absMatrix );
+ MatrixSetColumn( vAbsOrigin, 3, absMatrix );
+
+
+ // Transform the matrix into local space.
+ matrix3x4_t worldToEntity, local;
+ MatrixInvert( EntityToWorldTransform(), worldToEntity );
+ ConcatTransforms( worldToEntity, absMatrix, local );
+
+
+ // Suck out the origin and angles.
+ pVehicleEyeOrigin->Init( local[0][3], local[1][3], local[2][3] );
+ MatrixAngles( local, *pVehicleEyeAngles );
+
+ return bUsingThirdPersonCamera;
+}
+
+bool CBaseTFVehicle::GetRoleAbsViewPosition( int nRole, Vector *pAbsVehicleEyeOrigin, QAngle *pAbsVehicleEyeAngles )
+{
+ int iAttachment = LookupAttachment( "ThirdPersonCameraOrigin" );
+ if ( ShouldUseThirdPersonVehicleView() && vehicle_thirdperson.GetInt() && nRole == VEHICLE_DRIVER && iAttachment > 0 )
+ {
+ // Ok, we're using third person. Leave their angles intact but rotate theirt view around the
+ // ThirdPersonCameraOrigin attachment.
+ Vector vAttachOrigin;
+ QAngle vAttachAngles;
+ GetAttachment( iAttachment, vAttachOrigin, vAttachAngles );
+
+ Vector vForward, vRight, vUp;
+ AngleVectors( *pAbsVehicleEyeAngles, &vForward, &vRight, &vUp );
+
+ *pAbsVehicleEyeOrigin = vAttachOrigin + vForward * vehicle_view_offset_forward.GetFloat() +
+ vRight * vehicle_view_offset_right.GetFloat() +
+ vUp * vehicle_view_offset_up.GetFloat();
+
+ // Returning true tells the caller that we're using a third-person camera origin.
+ return true;
+ }
+ else
+ {
+ // Use the vehicle_eyes_passengerX attachments.
+ Assert( nRole >= 0 );
+ char pAttachmentName[32];
+ Q_snprintf( pAttachmentName, sizeof( pAttachmentName ), "vehicle_eyes_passenger%d", nRole );
+ int eyeAttachmentIndex = LookupAttachment(pAttachmentName);
+
+ QAngle vTempAngles;
+ GetAttachment( eyeAttachmentIndex, *pAbsVehicleEyeOrigin, vTempAngles );
+
+ if ( vehicle_attach_eye_angles.GetInt() )
+ *pAbsVehicleEyeAngles = vTempAngles;
+
+ return false;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Modify the player view/camera while in a vehicle
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::GetVehicleViewPosition( int nRole, Vector *pAbsOrigin, QAngle *pAbsAngles, float *pFOV /*= NULL*/ )
+{
+ // UNDONE: Use attachment point on the vehicle, not hardcoded player eyes
+ Assert( nRole >= 0 );
+ CBasePlayer *pPlayer = GetPassenger( nRole );
+ Assert( pPlayer );
+
+ Vector vehicleEyeOrigin;
+ QAngle vehicleEyeAngles = pPlayer->LocalEyeAngles();
+
+ GetRoleAbsViewPosition( nRole, &vehicleEyeOrigin, &vehicleEyeAngles );
+
+ *pAbsOrigin = vehicleEyeOrigin;
+ *pAbsAngles = vehicleEyeAngles;
+
+ /*
+ if ( bUsingThirdPersonCamera )
+ {
+ *pAbsOrigin = vehicleEyeOrigin;
+ *pAbsAngles = vehicleEyeAngles;
+ }
+ else
+ {
+ matrix3x4_t vehicleEyePosToWorld;
+
+ AngleMatrix( vehicleEyeAngles, vehicleEyePosToWorld );
+
+ // Compute the relative rotation between the unperterbed eye attachment + the eye angles
+ matrix3x4_t cameraToWorld;
+ AngleMatrix( *pAbsAngles, cameraToWorld );
+
+ matrix3x4_t worldToEyePos;
+ MatrixInvert( vehicleEyePosToWorld, worldToEyePos );
+
+ matrix3x4_t vehicleCameraToEyePos;
+ ConcatTransforms( worldToEyePos, cameraToWorld, vehicleCameraToEyePos );
+
+ // Now perterb the attachment point
+ if( inv_demo.GetInt() )
+ {
+ vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO * road_feel.GetFloat(), PITCH_CURVE_LINEAR, vehicleEyeAngles.x );
+ vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO * road_feel.GetFloat(), ROLL_CURVE_LINEAR, vehicleEyeAngles.z );
+ }
+ else
+ {
+ vehicleEyeAngles.x = RemapAngleRange( PITCH_CURVE_ZERO, PITCH_CURVE_LINEAR, vehicleEyeAngles.x );
+ vehicleEyeAngles.z = RemapAngleRange( ROLL_CURVE_ZERO, ROLL_CURVE_LINEAR, vehicleEyeAngles.z );
+ }
+
+ AngleMatrix( vehicleEyeAngles, vehicleEyeOrigin, vehicleEyePosToWorld );
+
+ // Now treat the relative eye angles as being relative to this new, perterbed view position...
+ matrix3x4_t newCameraToWorld;
+ ConcatTransforms( vehicleEyePosToWorld, vehicleCameraToEyePos, newCameraToWorld );
+
+ // output new view abs angles
+ MatrixAngles( newCameraToWorld, *pAbsAngles );
+
+ // UNDONE: *pOrigin would already be correct in single player if the HandleView() on the server ran after vphysics
+ MatrixGetColumn( newCameraToWorld, 3, *pAbsOrigin );
+ }
+ */
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Client-only code here
+//
+//-----------------------------------------------------------------------------
+
+#if defined (CLIENT_DLL)
+
+void CBaseTFVehicle::ClientThink()
+{
+ BaseClass::ClientThink();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseTFVehicle::ShouldPredict( void )
+{
+ // Only predict vehicles driven by local players
+ return GetDriverPlayer() ? GetDriverPlayer()->IsLocalPlayer() : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the angles that a player in the specified role should be using for visuals
+//-----------------------------------------------------------------------------
+QAngle CBaseTFVehicle::GetPassengerAngles( QAngle angCurrent, int nRole )
+{
+ // Just use your current angles
+ return angCurrent;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::DrawHudElements( void )
+{
+ // If we've got a driver gun, tell it to draw it's elements
+ if ( m_hDriverGun )
+ {
+ m_hDriverGun->DrawHudElements();
+ }
+
+ DrawHudBoostData();
+ SetupCrosshair();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::DrawHudBoostData( void )
+{
+#define HUD_IMAGE_LEFT XRES( 568 )
+
+ // Boostable vehicle
+ if ( IsBoostable() )
+ {
+ // Set our color
+ CHudTexture *pVehicleBoostLabel = gHUD.GetIcon( "no2" );
+ if ( pVehicleBoostLabel )
+ {
+ int nScreenY = ScreenHeight() - YRES( 12 );
+ float nOneHudHeight = ( YRES(10) + pVehicleBoostLabel->Height() );
+ nScreenY -= ( nOneHudHeight * 3 );
+
+ pVehicleBoostLabel->DrawSelf( HUD_IMAGE_LEFT, nScreenY - pVehicleBoostLabel->Height(), gHUD.m_clrNormal );
+ gHUD.DrawProgressBar( HUD_IMAGE_LEFT, nScreenY + YRES( 4 ), XRES( 70 ), YRES( 4 ), m_nBoostTimeLeft / 100.0f, gHUD.m_clrNormal, CHud::HUDPB_HORIZONTAL_INV );
+ }
+ }
+
+#undef HUD_IMAGE_LEFT
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a crosshair when in a vehicle and we don't have a proper
+// crosshair sprite (ie. a commando laser rifle).
+//-----------------------------------------------------------------------------
+void CBaseTFVehicle::SetupCrosshair( void )
+{
+ if ( !m_pIconDefaultCrosshair )
+ {
+ // Init the default crosshair the first time.
+ CHudTexture newTexture;
+ Q_strncpy( newTexture.szTextureFile, "sprites/crosshairs", sizeof( newTexture.szTextureFile ) );
+
+ newTexture.rc.left = 0;
+ newTexture.rc.top = 48;
+ newTexture.rc.right = newTexture.rc.left + 24;
+ newTexture.rc.bottom = newTexture.rc.top + 24;
+ m_pIconDefaultCrosshair = gHUD.AddUnsearchableHudIconToList( newTexture );
+ }
+
+ CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair );
+ if ( crosshair )
+ {
+ if ( !crosshair->HasCrosshair() && m_pIconDefaultCrosshair )
+ {
+ crosshair->SetCrosshair( m_pIconDefaultCrosshair, gHUD.m_clrNormal );
+ }
+ }
+}
+
+#endif
diff --git a/game/shared/tf2/basetfvehicle.h b/game/shared/tf2/basetfvehicle.h
new file mode 100644
index 0000000..47c5dae
--- /dev/null
+++ b/game/shared/tf2/basetfvehicle.h
@@ -0,0 +1,272 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A base vehicle class
+//
+//=============================================================================//
+
+#ifndef BASETFVEHICLE_H
+#define BASETFVEHICLE_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetfplayer_shared.h"
+#include "baseobject_shared.h"
+#include "tf_obj_basedrivergun_shared.h"
+
+#if defined( CLIENT_DLL )
+#include "iclientvehicle.h"
+#else
+#include "IServerVehicle.h"
+#endif
+
+
+class CMoveData;
+class CUserCmd;
+class CBasePlayer;
+
+#if defined( CLIENT_DLL )
+#define CBaseTFVehicle C_BaseTFVehicle
+#endif
+
+class CBaseTFVehicle;
+class CBaseObjectDriverGun;
+
+struct VehicleBaseMoveData_t
+{
+ CBaseTFVehicle *m_pVehicle;
+};
+
+// ------------------------------------------------------------------------ //
+// The base class that all vehicles in tf2 will derive from
+// ------------------------------------------------------------------------ //
+#if defined( CLIENT_DLL )
+class CBaseTFVehicle : public CBaseObject, public IClientVehicle
+#else
+class CBaseTFVehicle : public CBaseObject, public IServerVehicle
+#endif
+{
+ DECLARE_CLASS( CBaseTFVehicle, CBaseObject );
+
+public:
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CBaseTFVehicle();
+
+#if !defined (CLIENT_DLL)
+ // CBaseEntity overrides
+ virtual void FinishedBuilding( void );
+ virtual void DestroyObject( );
+ virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ virtual bool UseAttachedItem( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ virtual void GetVectors(Vector* pForward, Vector* pRight, Vector* pUp) const;
+
+ virtual bool ClientCommand( CBaseTFPlayer *pPlayer, const CCommand &args );
+
+ // IVehicle overrides
+ virtual IServerVehicle* GetServerVehicle() { return this; }
+
+ virtual CBaseEntity* GetVehicleEnt();
+
+ // Get and set the current driver.
+ virtual void SetPassenger( int nRole, CBasePlayer *pEnt );
+
+ // Where do we get out of the vehicle?
+ virtual bool GetPassengerExitPoint( int nRole, Vector *pExitPoint, QAngle *pAngles );
+
+ virtual Class_T ClassifyPassenger( CBasePlayer *pPassenger, Class_T defaultClassification ) { return defaultClassification; }
+ virtual float DamageModifier ( CTakeDamageInfo &info ) { return 1.0; }
+ virtual const vehicleparams_t *GetVehicleParams( void ) { return NULL; }
+
+ virtual bool IsVehicleUpright( void ) { return true; }
+ virtual bool IsPassengerEntering( void ) { Assert( 0 ); return true; }
+ virtual bool IsPassengerExiting( void ) { Assert( 0 ); return true; }
+
+ // NPC Driving
+ virtual bool NPC_CanDrive( void ) { return true; }
+ virtual void NPC_SetDriver( CNPC_VehicleDriver *pDriver ) { return; }
+ virtual void NPC_DriveVehicle( void ) { return; }
+ virtual void NPC_ThrottleCenter( void ) { return; }
+ virtual void NPC_ThrottleReverse( void ) { return; }
+ virtual void NPC_ThrottleForward( void ) { return; }
+ virtual void NPC_Brake( void ) { return; }
+ virtual void NPC_TurnLeft( float flDegrees ) { return; }
+ virtual void NPC_TurnRight( float flDegrees ) { return; }
+ virtual void NPC_TurnCenter( void ) { return; }
+ virtual void NPC_PrimaryFire( void ) { return; }
+ virtual void NPC_SecondaryFire( void ) { return; }
+ virtual bool NPC_HasPrimaryWeapon( void ) { return false; }
+ virtual bool NPC_HasSecondaryWeapon( void ) { return false; }
+ virtual void NPC_AimPrimaryWeapon( Vector vecTarget ) { return; }
+ virtual void NPC_AimSecondaryWeapon( Vector vecTarget ) { return; }
+
+ // Weapon handling
+ virtual void Weapon_PrimaryRanges( float *flMinRange, float *flMaxRange ) { *flMinRange = 0; *flMaxRange = 0; }
+ virtual void Weapon_SecondaryRanges( float *flMinRange, float *flMaxRange ) { *flMinRange = 0; *flMaxRange = 0; }
+ virtual float Weapon_PrimaryCanFireAt( void ) { return gpGlobals->curtime; } // Return the time at which this vehicle's primary weapon can fire again
+ virtual float Weapon_SecondaryCanFireAt( void ) { return gpGlobals->curtime; } // Return the time at which this vehicle's secondary weapon can fire again
+
+ // Vehicles dont want to attach to anything they're built upon
+ virtual bool ShouldAttachToParent( void ) { return false; }
+
+ virtual bool MustNotBeBuiltInConstructionYard( void ) const { return false; }
+
+ // Purpose: Try to board the vehicle
+ void AttemptToBoardVehicle( CBaseTFPlayer *pPlayer );
+
+ // Figure out which role of a parent vehicle this vehicle is sitting in..
+ int GetParentVehicleRole();
+
+ // Purpose:
+ void GetPassengerExitPoint( CBasePlayer *pPlayer, int nRole, Vector *pAbsPosition, QAngle *pAbsAngles );
+ int GetEntryAnimForPoint( const Vector &vecPoint );
+ int GetExitAnimToUse( Vector &vecEyeExitEndpoint, bool &bAllPointsBlocked );
+ void HandleEntryExitFinish( bool bExitAnimOn, bool bResetAnim );
+ void HandlePassengerEntry( CBasePlayer *pPlayer, bool bAllowEntryOutsideZone = false );
+ bool HandlePassengerExit( CBasePlayer *pPlayer );
+
+ // Deterioration
+ void VehicleDeteriorationThink( void );
+ void VehiclePassengerThink( void );
+
+#endif
+
+ bool IsReadyToDrive( void );
+
+ virtual bool IsAVehicle( void ) { return true; }
+
+ // Get a position in *local space* inside the vehicle for the player to start at
+ virtual void GetPassengerStartPoint( int nRole, Vector *pPoint, QAngle *pAngles );
+
+#if defined( CLIENT_DLL )
+ // C_BaseEntity overrides
+ virtual IClientVehicle* GetClientVehicle() { return this; }
+
+ virtual C_BaseEntity *GetVehicleEnt();
+
+ virtual void ClientThink();
+
+ // Fills in the unperterbed view position for a particular role.
+
+ // Prediction
+ virtual bool ShouldPredict( void );
+ virtual bool IsPredicted( void ) const { return true; }
+
+ // IClientVehicle
+
+ // Called at time player enters vehicle
+ virtual void GetVehicleFOV( float &flFOV ) { return; }
+ virtual void DrawHudElements( void );
+
+ // Get the angles that a player in the specified role should be using for visuals
+ virtual QAngle GetPassengerAngles( QAngle angCurrent, int nRole );
+
+ // Allows the vehicle to restrict view angles
+ virtual void UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd ) {}
+ virtual void GetVehicleClipPlanes( float &flZNear, float &flZFar ) const {}
+
+ bool IsBoostable( void ) { return m_bBoostUpgrade; }
+
+ // Hud
+ virtual void DrawHudBoostData( void );
+ virtual void SetupCrosshair( void );
+
+#endif
+
+ int LocateEntryPoint( CBaseTFPlayer *pPlayer, float* fBest2dDistanceSqr= NULL );
+
+ // This lets the object decide whether or not it wants to use the ThirdPersonCameraOrigin attachment for its view.
+ // The manned guns use first-person when they're on the ground and third-person when they're in a vehicle.
+ virtual bool ShouldUseThirdPersonVehicleView();
+ virtual void GetVehicleViewPosition( int nRole, Vector *pOrigin, QAngle *pAngles, float *pFOV = NULL );
+ virtual bool GetRoleViewPosition( int nRole, Vector *pVehicleEyeOrigin, QAngle *pVehicleEyeAngles );
+ virtual bool GetRoleAbsViewPosition( int nRole, Vector *pAbsVehicleEyeOrigin, QAngle *pAbsVehicleEyeAngles );
+
+ // Can a given passenger take damage?
+ virtual bool IsPassengerDamagable( int nRole ) { return (nRole != VEHICLE_DRIVER); }
+
+
+ virtual void Spawn();
+ virtual void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move );
+ virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove ) {}
+ virtual void FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move );
+ virtual void ItemPostFrame( CBasePlayer *pPassenger );
+
+ virtual CBasePlayer* GetPassenger( int nRole = VEHICLE_DRIVER );
+ virtual int GetPassengerRole( CBasePlayer *pEnt );
+
+ // Does the player use his normal weapons while in this mode?
+ virtual bool IsPassengerUsingStandardWeapons( int nRole = VEHICLE_DRIVER ) { return false; }
+
+ virtual Vector GetSoundEmissionOrigin() const;
+
+ // Returns the driver as a tfplayer pointer
+ CBaseTFPlayer *GetDriverPlayer();
+
+ int GetMaxPassengerCount() const;
+ int GetPassengerCount() const;
+
+ // Is a particular player in the vehicle?
+ bool IsPlayerInVehicle( CBaseTFPlayer *pPlayer );
+
+ void ResetDeteriorationTime( void );
+
+ // Driver controlled guns
+ void SetDriverGun( CBaseObjectDriverGun *pGun );
+ void VehicleDriverGunThink( void );
+
+protected:
+ enum
+ {
+ MAX_PASSENGERS = 4,
+ MAX_PASSENGER_BITS = 3
+ };
+
+ // Can we get into the vehicle?
+ virtual bool CanGetInVehicle( CBaseTFPlayer *pPlayer );
+
+ // Here's where we deal with weapons
+ virtual void OnItemPostFrame( CBaseTFPlayer *pDriver );
+
+ // Specify the number of roles we can have
+ void SetMaxPassengerCount( int nMaxPassengers );
+
+ bool IsValidExitPoint( int nRole, Vector *pExitPoint, QAngle *pAngles );
+ int GetEmptyRole( void );
+
+private:
+#if !defined (CLIENT_DLL)
+ // Get the parent vehicle of this vehicle..
+ CBaseTFVehicle *GetParentVehicle();
+
+ // Get a position in *world space* inside the vehicle for the player to exit at
+ void GetInitialPassengerExitPoint( int nRole, Vector *pAbsPoint, QAngle *pAbsAngles );
+
+ // Figure out which role of a vehicle a child vehicle is sitting in..
+ int GetChildVehicleRole( CBaseTFVehicle *pChild );
+#endif
+
+private:
+ typedef CHandle<CBaseTFPlayer> CPlayerHandle;
+ CNetworkArray( CPlayerHandle, m_hPassengers, MAX_PASSENGERS );
+ CNetworkVar( int, m_nMaxPassengers );
+
+ // Driver controlled gun
+ CNetworkHandle( CBaseObjectDriverGun, m_hDriverGun );
+
+#if defined( CLIENT_DLL )
+
+ CHudTexture *m_pIconDefaultCrosshair;
+
+ bool m_bBoostUpgrade;
+ int m_nBoostTimeLeft;
+
+private:
+ CBaseTFVehicle( const CBaseTFVehicle & ); // not defined, not accessible
+#endif
+};
+
+#endif // BASETFVEHICLE_H
diff --git a/game/shared/tf2/env_laserdesignation.cpp b/game/shared/tf2/env_laserdesignation.cpp
new file mode 100644
index 0000000..543be1a
--- /dev/null
+++ b/game/shared/tf2/env_laserdesignation.cpp
@@ -0,0 +1,328 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Entity used to highlight laser designation points to clients
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "tf_obj_manned_plasmagun.h"
+#include "env_laserdesignation.h"
+
+#if !defined( CLIENT_DLL )
+
+#include "tf_vehicle_tank.h"
+#include "tf_obj_manned_missilelauncher.h"
+
+#endif
+
+extern ConVar weapon_grenade_rocket_track_range_mod;
+extern ConVar vehicle_tank_range;
+extern ConVar weapon_rocket_launcher_range;
+extern ConVar obj_manned_missilelauncher_range_off;
+
+//-----------------------------------------------------------------------------
+// Stores a list of all laser designations
+//-----------------------------------------------------------------------------
+CUtlVector< EHANDLE > CEnvLaserDesignation::m_LaserDesignatorsTeam1;
+CUtlVector< EHANDLE > CEnvLaserDesignation::m_LaserDesignatorsTeam2;
+
+IMPLEMENT_NETWORKCLASS_ALIASED( EnvLaserDesignation, DT_EnvLaserDesignation )
+
+BEGIN_NETWORK_TABLE( CEnvLaserDesignation, DT_EnvLaserDesignation )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO( m_bActive ), 1, SPROP_UNSIGNED ),
+#else
+ RecvPropInt( RECVINFO( m_bActive ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CEnvLaserDesignation )
+ DEFINE_PRED_FIELD( m_bActive, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( env_laserdesignation, CEnvLaserDesignation );
+PRECACHE_REGISTER( env_laserdesignation );
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a laser designation
+//-----------------------------------------------------------------------------
+CEnvLaserDesignation *CEnvLaserDesignation::Create( CBasePlayer *pOwner )
+{
+ CEnvLaserDesignation *pDesignation = (CEnvLaserDesignation*)CreateEntityByName("env_laserdesignation");
+ pDesignation->Spawn();
+ pDesignation->SetOwnerEntity( pOwner );
+ pDesignation->ChangeTeam( pOwner->GetTeamNumber() );
+ pDesignation->SetActive( false );
+
+ return pDesignation;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a laser designation
+//-----------------------------------------------------------------------------
+CEnvLaserDesignation *CEnvLaserDesignation::CreatePredicted( CBasePlayer *pOwner )
+{
+#if !defined( NO_ENTITY_PREDICTION )
+ CEnvLaserDesignation *pDesignation = (CEnvLaserDesignation*)CREATE_PREDICTED_ENTITY("env_laserdesignation");
+ if ( pDesignation )
+ {
+ pDesignation->Spawn();
+ pDesignation->SetOwnerEntity( pOwner );
+ pDesignation->SetPlayerSimulated( pOwner );
+ pDesignation->ChangeTeam( pOwner->GetTeamNumber() );
+ pDesignation->SetActive( false );
+ }
+
+ return pDesignation;
+#else
+ return NULL;
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEnvLaserDesignation::CEnvLaserDesignation( void )
+{
+ m_bActive = -1; // So the first setactive will take effect
+ m_bPrevActive = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CEnvLaserDesignation::~CEnvLaserDesignation( void )
+{
+ EHANDLE hLaser;
+ hLaser = this;
+
+ if ( GetTeamNumber() == 1 )
+ {
+ m_LaserDesignatorsTeam1.FindAndRemove( hLaser );
+ }
+ else if ( GetTeamNumber() == 2 )
+ {
+ m_LaserDesignatorsTeam2.FindAndRemove( hLaser );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEnvLaserDesignation::Spawn( void )
+{
+ SetModel( "models/projectiles/grenade_limpet.mdl" );
+ SetMoveType( MOVETYPE_NONE );
+ SetSolid( SOLID_NONE );
+ SetSize( vec3_origin, vec3_origin );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEnvLaserDesignation::ChangeTeam( int iTeamNum )
+{
+ Assert( iTeamNum > 0 && iTeamNum < MAX_TF_TEAMS );
+
+ EHANDLE hLaser;
+ hLaser = this;
+ if ( iTeamNum == 1 )
+ {
+ m_LaserDesignatorsTeam1.AddToTail( hLaser );
+ }
+ else if ( iTeamNum == 2 )
+ {
+ m_LaserDesignatorsTeam2.AddToTail( hLaser );
+ }
+
+ BaseClass::ChangeTeam( iTeamNum );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CEnvLaserDesignation::SetActive( bool bActive )
+{
+ if ( bActive == m_bActive )
+ return;
+
+ if ( !bActive )
+ {
+ AddEffects( EF_NODRAW );
+ }
+ else
+ {
+ IncrementInterpolationFrame();
+ RemoveEffects( EF_NODRAW );
+ }
+
+#if defined( CLIENT_DLL )
+ ENTITY_PANEL_ACTIVATE( "laserdesignation", bActive );
+#endif
+
+ m_bActive = bActive;
+}
+
+#if !defined( CLIENT_DLL )
+
+
+int CEnvLaserDesignation::UpdateTransmitState()
+{
+ return SetTransmitState( FL_EDICT_FULLCHECK );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEnvLaserDesignation::ShouldTransmit( const CCheckTransmitInfo *pInfo )
+{
+ // Only transmit to players who care about laser designation:
+ // - Player designating
+ // - Players in tanks
+ // - Commandos
+ CBaseEntity* pRecipientEntity = CBaseEntity::Instance( pInfo->m_pClientEnt );
+ if ( pRecipientEntity->IsPlayer() )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)pRecipientEntity;
+
+ // Designating player?
+ if ( pPlayer == GetOwnerEntity() )
+ return SetTransmitState( FL_EDICT_ALWAYS );
+
+ if ( !InSameTeam( pPlayer ) )
+ return FL_EDICT_DONTSEND;
+
+ // In a tank?
+ if ( pPlayer->IsInAVehicle() )
+ {
+ CBaseEntity *pVehicle = pPlayer->GetVehicle()->GetVehicleEnt();
+ if ( dynamic_cast<CVehicleTank*>(pVehicle) )
+ {
+ // Make sure it's within range of the tank's fire
+ static float flTankRange = 0;
+ if ( !flTankRange )
+ {
+ flTankRange = vehicle_tank_range.GetFloat() * weapon_grenade_rocket_track_range_mod.GetFloat();
+ flTankRange *= flTankRange;
+ }
+
+ float flDistanceSqr = ( GetAbsOrigin() - pPlayer->GetAbsOrigin() ).LengthSqr();
+ if ( flDistanceSqr < flTankRange )
+ return FL_EDICT_ALWAYS;
+ }
+ else if ( dynamic_cast<CObjectMannedMissileLauncher*>(pVehicle) )
+ {
+ // Make sure it's within range of the manned missile launcher's fire
+ static float flGunRange = 0;
+ if ( !flGunRange )
+ {
+ flGunRange = obj_manned_missilelauncher_range_off.GetFloat() * weapon_grenade_rocket_track_range_mod.GetFloat();
+ flGunRange *= flGunRange;
+ }
+
+ float flDistanceSqr = ( GetAbsOrigin() - pPlayer->GetAbsOrigin() ).LengthSqr();
+ if ( flDistanceSqr < flGunRange )
+ return FL_EDICT_ALWAYS;
+ }
+ }
+
+ // Is the player a commando?
+ if ( pPlayer->PlayerClass() == TFCLASS_COMMANDO )
+ {
+ // Make sure it's within range of the commando's rockets
+ static float flCommandoRange = 0;
+ if ( !flCommandoRange )
+ {
+ flCommandoRange = weapon_rocket_launcher_range.GetFloat() * weapon_grenade_rocket_track_range_mod.GetFloat();
+ flCommandoRange *= flCommandoRange;
+ }
+
+ float flDistanceSqr = ( GetAbsOrigin() - pPlayer->GetAbsOrigin() ).LengthSqr();
+ if ( flDistanceSqr < flCommandoRange )
+ return FL_EDICT_ALWAYS;
+ }
+ }
+
+ return FL_EDICT_DONTSEND;
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEnvLaserDesignation::GetNumLaserDesignators( int iTeamNumber )
+{
+ Assert( iTeamNumber > 0 && iTeamNumber < MAX_TF_TEAMS );
+
+ if ( iTeamNumber == 1 )
+ return m_LaserDesignatorsTeam1.Count();
+
+ return m_LaserDesignatorsTeam2.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CEnvLaserDesignation::GetLaserDesignation( int iTeamNumber, int iDesignator, Vector *vecOrigin )
+{
+ Assert( iTeamNumber > 0 && iTeamNumber < MAX_TF_TEAMS );
+
+ CHandle<CEnvLaserDesignation> hLaser;
+ if ( iTeamNumber == 1 )
+ {
+ Assert( iDesignator < m_LaserDesignatorsTeam1.Count() );
+ hLaser = m_LaserDesignatorsTeam1[iDesignator];
+ }
+ else
+ {
+ Assert( iDesignator < m_LaserDesignatorsTeam2.Count() );
+ hLaser = m_LaserDesignatorsTeam2[iDesignator];
+ }
+
+ // Active?
+ if ( !hLaser.Get() || !hLaser->IsActive() )
+ return false;
+
+ *vecOrigin = hLaser->GetAbsOrigin();
+ return true;
+}
+
+#if defined( CLIENT_DLL )
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CEnvLaserDesignation::DrawModel( int flags )
+{
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void CEnvLaserDesignation::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( m_bActive != m_bPrevActive )
+ {
+ ENTITY_PANEL_ACTIVATE( "laserdesignation", m_bActive );
+ }
+ m_bPrevActive = m_bActive.Get();
+}
+
+//-----------------------------------------------------------------------------
+// Add, remove object from the panel
+//-----------------------------------------------------------------------------
+void CEnvLaserDesignation::SetDormant( bool bDormant )
+{
+ BaseClass::SetDormant( bDormant );
+
+ ENTITY_PANEL_ACTIVATE( "laserdesignation", (!bDormant && m_bActive) );
+}
+
+#endif \ No newline at end of file
diff --git a/game/shared/tf2/env_laserdesignation.h b/game/shared/tf2/env_laserdesignation.h
new file mode 100644
index 0000000..c715498
--- /dev/null
+++ b/game/shared/tf2/env_laserdesignation.h
@@ -0,0 +1,80 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef ENV_LASERDESIGNATION_H
+#define ENV_LASERDESIGNATION_H
+#pragma once
+
+#if defined( CLIENT_DLL )
+#define CEnvLaserDesignation C_EnvLaserDesignation
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: A laser designation point
+//-----------------------------------------------------------------------------
+class CEnvLaserDesignation : public CBaseAnimating
+{
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+ DECLARE_CLASS( CEnvLaserDesignation, CBaseAnimating );
+
+ CEnvLaserDesignation( void );
+ ~CEnvLaserDesignation( void );
+
+ virtual void Spawn( void );
+ virtual void ChangeTeam( int iTeamNum );
+
+ // Designation
+ void SetActive( bool bActive );
+ bool IsActive( void ) { return m_bActive; }
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwnerEntity() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ DECLARE_ENTITY_PANEL();
+
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void SetDormant( bool bDormant );
+
+ virtual int DrawModel( int flags );
+#else
+ virtual int UpdateTransmitState();
+ virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
+#endif
+
+ // Global Designator access
+ static CEnvLaserDesignation *Create( CBasePlayer *pOwner );
+ static CEnvLaserDesignation *CreatePredicted( CBasePlayer *pOwner );
+ static int GetNumLaserDesignators( int iTeamNumber );
+ static bool GetLaserDesignation( int iTeamNumber, int iDesignator, Vector *vecOrigin );
+
+protected:
+ static CUtlVector< EHANDLE > m_LaserDesignatorsTeam1;
+ static CUtlVector< EHANDLE > m_LaserDesignatorsTeam2;
+
+ CNetworkVar( bool, m_bActive );
+
+ bool m_bPrevActive;
+private:
+ CEnvLaserDesignation( const CEnvLaserDesignation& src );
+};
+
+#endif // ENV_LASERDESIGNATION_H
diff --git a/game/shared/tf2/gasoline_shared.h b/game/shared/tf2/gasoline_shared.h
new file mode 100644
index 0000000..efd9280
--- /dev/null
+++ b/game/shared/tf2/gasoline_shared.h
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GASOLINE_SHARED_H
+#define GASOLINE_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#define PYRO_AMMO_TYPE "Gasoline"
+
+
+// Radius in inches of each gasoline blob. They should render to about this size.
+#define GASOLINE_BLOB_RADIUS 30
+
+// Blobs start to attract each other when their centerpoints get this close.
+#define GASOLINE_ATTRACT_START_DISTANCE (GASOLINE_BLOB_RADIUS + 20)
+
+
+// Blobs expire after this long whether they are lit or not.
+#define MAX_LIT_GASOLINE_BLOB_LIFETIME 5.0
+#define MAX_UNLIT_GASOLINE_BLOB_LIFETIME 20.0
+
+
+// Heat per second given off by fire.
+#define FIRE_DAMAGE_PER_SEC 85
+
+
+#define BLOBFLAG_LIT 0x01 // This blob is on fire.
+#define BLOBFLAG_STOPPED 0x02 // This means it has hit a surface and stopped moving.
+#define BLOBFLAG_USE_GRAVITY 0x04
+#define NUM_BLOB_FLAGS 3
+
+
+#endif // GASOLINE_SHARED_H
diff --git a/game/shared/tf2/grenade_antipersonnel.cpp b/game/shared/tf2/grenade_antipersonnel.cpp
new file mode 100644
index 0000000..21ecd9b
--- /dev/null
+++ b/game/shared/tf2/grenade_antipersonnel.cpp
@@ -0,0 +1,211 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "tf_basecombatweapon.h"
+#include "basegrenade_shared.h"
+#include "engine/IEngineSound.h"
+#include "tf_shareddefs.h"
+#include "IEffects.h"
+#include "Sprite.h"
+#include "grenade_antipersonnel.h"
+
+// Damage CVars
+ConVar weapon_antipersonnel_grenade_damage( "weapon_antipersonnel_grenade_damage","0", FCVAR_NONE, "Anti-personnel grenade maximum damage" );
+ConVar weapon_antipersonnel_grenade_radius( "weapon_antipersonnel_grenade_radius","0", FCVAR_NONE, "Anti-personnel grenade splash radius" );
+
+#if !defined( CLIENT_DLL )
+// Server Only
+ConVar weapon_antipersonnel_grenade_force( "weapon_antipersonnel_grenade_force","225.0", FCVAR_NONE, "Grenade explosive force modifier." );
+#endif
+
+
+IMPLEMENT_SERVERCLASS_ST(CGrenadeAntiPersonnel, DT_GrenadeAntiPersonnel)
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( grenade_antipersonnel, CGrenadeAntiPersonnel );
+PRECACHE_WEAPON_REGISTER(grenade_antipersonnel);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CGrenadeAntiPersonnel::CGrenadeAntiPersonnel()
+{
+ UseClientSideAnimation();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeAntiPersonnel::Precache( void )
+{
+ BaseClass::Precache( );
+
+ PrecacheModel( "models/weapons/w_grenade.mdl" );
+ PrecacheModel( "sprites/redglow1.vmt" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeAntiPersonnel::Spawn( void )
+{
+ Precache();
+
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
+ SetSolid( SOLID_BBOX );
+ SetGravity( 1.0 );
+ SetFriction( 0.9 );
+ SetElasticity( 2.0f );
+ SetModel( "models/weapons/w_grenade.mdl" );
+ UTIL_SetSize(this, vec3_origin, vec3_origin);
+ SetTouch( BounceTouch );
+ SetCollisionGroup( TFCOLLISION_GROUP_GRENADE );
+
+ m_flDetonateTime = gpGlobals->curtime + 3.0;
+ SetThink( TumbleThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ // Set my damages to the cvar values
+ SetDamage( weapon_antipersonnel_grenade_damage.GetFloat() );
+ SetDamageRadius( weapon_antipersonnel_grenade_radius.GetFloat() );
+
+ // Create a green light
+ m_pLiveSprite = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin() + Vector(0,0,1), false );
+ m_pLiveSprite->SetTransparency( kRenderGlow, 0, 255, 0, 128, kRenderFxNoDissipation );
+ m_pLiveSprite->SetBrightness( 255 );
+ m_pLiveSprite->SetScale( 1 );
+ m_pLiveSprite->SetAttachment( this, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeAntiPersonnel::UpdateOnRemove( void )
+{
+ // Remove our live sprite
+ if ( m_pLiveSprite )
+ {
+ UTIL_Remove( m_pLiveSprite );
+ m_pLiveSprite = NULL;
+ }
+
+ // Chain at end to mimic destructor unwind order
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allow shield parry's
+//-----------------------------------------------------------------------------
+void CGrenadeAntiPersonnel::BounceTouch( CBaseEntity *pOther )
+{
+ // Don't blow up on trigger brushes
+ Assert( pOther );
+ if ( !pOther->IsSolid() )
+ return;
+
+ if ( pOther->GetCollisionGroup() == TFCOLLISION_GROUP_SHIELD )
+ {
+ // Move away from the shield...
+ // Fling it out a little extra along the plane normal
+ Vector vecCenter;
+ AngleVectors( pOther->GetAbsAngles(), &vecCenter );
+
+ // Bounce off the ground if it's on the ground...
+ Vector vecNewVelocity = GetAbsVelocity();
+ VectorMultiply( vecCenter, 400.0f, vecNewVelocity );
+ if ((GetFlags() & FL_ONGROUND) && vecNewVelocity.z <= 100.0f)
+ {
+ vecNewVelocity.z = 100.0f;
+ }
+ SetAbsVelocity( vecNewVelocity );
+ }
+
+ // If we're set to explode on contact, and we just hit an enemy, go kaboom
+ if ( m_bExplodeOnContact && !InSameTeam(pOther) && pOther->m_takedamage != DAMAGE_NO )
+ {
+ Detonate();
+ return;
+ }
+
+ BaseClass::BounceTouch( pOther );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the radius for the screenshake
+//-----------------------------------------------------------------------------
+float CGrenadeAntiPersonnel::GetShakeRadius( void )
+{
+ return (m_DmgRadius * 2);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a missile
+//-----------------------------------------------------------------------------
+void CGrenadeAntiPersonnel::Detonate( void )
+{
+ BaseClass::Detonate();
+
+ // iterate on all entities in the vicinity and find vehicles
+ CBaseEntity *pEntity = NULL;
+ for ( CEntitySphereQuery sphere( GetAbsOrigin(), m_DmgRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
+ {
+ // Check team.
+ if ( pEntity->GetTeam() == GetTeam() )
+ continue;
+
+ if ( pEntity->GetServerVehicle() )
+ {
+ IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
+ if ( pPhysObject )
+ {
+ // Rocket the vehicle in the direction of the incoming rocket.
+ Vector vecForceDir = pEntity->GetAbsOrigin() - GetAbsOrigin();
+ float flDistance = VectorNormalize( vecForceDir );
+
+ if ( flDistance >= 0.0f && flDistance < m_DmgRadius )
+ {
+ vecForceDir.z = 1.0f;
+ VectorNormalize( vecForceDir );
+
+ float flForce = pPhysObject->GetMass();
+ flForce += ( 4 * 500.0f ); // Wheels
+ flForce *= weapon_antipersonnel_grenade_force.GetFloat();
+ flForce *= ( 1.0f - ( flDistance / m_DmgRadius ) );
+
+ vecForceDir *= flForce;
+
+ pPhysObject->ApplyForceOffset( vecForceDir, GetAbsOrigin() );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a missile
+//-----------------------------------------------------------------------------
+CGrenadeAntiPersonnel *CGrenadeAntiPersonnel::Create( const Vector &vecOrigin, const Vector &vecForward, CBasePlayer *pOwner )
+{
+ CGrenadeAntiPersonnel *pGrenade = (CGrenadeAntiPersonnel*)CreateEntityByName("grenade_antipersonnel");
+
+ UTIL_SetOrigin( pGrenade, vecOrigin );
+ pGrenade->Spawn();
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+ pGrenade->SetOwnerEntity( pOwner );
+ pGrenade->SetThrower( pOwner );
+ pGrenade->SetAbsVelocity( vecForward );
+ QAngle angles;
+ VectorAngles( vecForward, angles );
+ pGrenade->SetLocalAngles( angles );
+ pGrenade->SetLocalAngularVelocity( RandomAngle(-500,500) );
+
+ return pGrenade;
+}
+
+
+
diff --git a/game/shared/tf2/grenade_antipersonnel.h b/game/shared/tf2/grenade_antipersonnel.h
new file mode 100644
index 0000000..2538f78
--- /dev/null
+++ b/game/shared/tf2/grenade_antipersonnel.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GRENADE_ANTIPERSONNEL_H
+#define GRENADE_ANTIPERSONNEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CSprite;
+
+#include "grenade_base_empable.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Antipersonnel grenade
+//-----------------------------------------------------------------------------
+class CGrenadeAntiPersonnel : public CBaseEMPableGrenade
+{
+ DECLARE_CLASS( CGrenadeAntiPersonnel, CBaseEMPableGrenade );
+public:
+ CGrenadeAntiPersonnel();
+
+ DECLARE_SERVERCLASS();
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+ virtual void UpdateOnRemove( void );
+ virtual void BounceTouch( CBaseEntity *pOther );
+// virtual void BounceSound( void );
+ virtual float GetShakeRadius( void );
+
+ virtual void Detonate( void );
+
+ // Damage type accessors
+ virtual int GetDamageType() const { return DMG_BLAST; }
+
+ static CGrenadeAntiPersonnel *CGrenadeAntiPersonnel::Create( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner );
+
+ void SetExplodeOnContact( bool bExplode ) { m_bExplodeOnContact = bExplode; }
+
+private:
+ CSprite *m_pLiveSprite;
+ bool m_bExplodeOnContact;
+};
+
+#endif // GRENADE_ANTIPERSONNEL_H
diff --git a/game/shared/tf2/grenade_base_empable.cpp b/game/shared/tf2/grenade_base_empable.cpp
new file mode 100644
index 0000000..2d46e80
--- /dev/null
+++ b/game/shared/tf2/grenade_base_empable.cpp
@@ -0,0 +1,89 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basegrenade_shared.h"
+#include "engine/IEngineSound.h"
+#include "grenade_base_empable.h"
+#include "IEffects.h"
+
+#if !defined( CLIENT_DLL )
+// Global Savedata
+BEGIN_DATADESC( CBaseEMPableGrenade )
+ // Function Pointers
+ DEFINE_THINKFUNC( FizzleThink ),
+END_DATADESC()
+#endif
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseEMPableGrenade, DT_BaseEMPableGrenade )
+
+BEGIN_NETWORK_TABLE( CBaseEMPableGrenade, DT_BaseEMPableGrenade )
+#if !defined( CLIENT_DLL )
+ SendPropFloat( SENDINFO( m_flFizzleDuration ), 10, SPROP_ROUNDDOWN, 0.0, 256.0f ),
+#else
+ RecvPropFloat( RECVINFO( m_flFizzleDuration ) ),
+#endif
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( base_empable_grenade, CBaseEMPableGrenade );
+
+BEGIN_PREDICTION_DATA( CBaseEMPableGrenade )
+
+ DEFINE_PRED_FIELD( m_flFizzleDuration, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+#define GRENADE_FIZZLE_DURATION 0.5
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseEMPableGrenade::CBaseEMPableGrenade( void )
+{
+ m_flFizzleDuration = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Apply EMP damage to class
+//-----------------------------------------------------------------------------
+bool CBaseEMPableGrenade::TakeEMPDamage( float duration )
+{
+ // If we're fizzling already, ignore extra EMP damage
+ if ( m_flFizzleDuration )
+ return true;
+
+ // Fizzle away in a couple of seconds
+ m_flFizzleDuration = gpGlobals->curtime + MIN( duration, GRENADE_FIZZLE_DURATION );
+ SetThink( FizzleThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Fizzle out and remove self from the world.
+//-----------------------------------------------------------------------------
+void CBaseEMPableGrenade::FizzleThink( void )
+{
+ float flDeltaTime = m_flFizzleDuration - gpGlobals->curtime;
+
+ // Keep fizzling until it's time to go
+ if ( flDeltaTime > 0.0f )
+ {
+ // Emit a fizzle sound
+ EmitSound( "BaseEMPableGrenade.Fizzle" );
+
+ // Smoke & Spark
+ g_pEffects->Sparks( GetAbsOrigin() );
+ UTIL_Smoke( GetAbsOrigin(), random->RandomInt( 4, 7), 10 );
+ }
+ else
+ {
+ Remove( );
+ return;
+ }
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
diff --git a/game/shared/tf2/grenade_base_empable.h b/game/shared/tf2/grenade_base_empable.h
new file mode 100644
index 0000000..3da030d
--- /dev/null
+++ b/game/shared/tf2/grenade_base_empable.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GRENADE_BASE_EMPABLE_H
+#define GRENADE_BASE_EMPABLE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basegrenade_shared.h"
+
+#if defined( CLIENT_DLL )
+#define CBaseEMPableGrenade C_BaseEMPableGrenade
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: EMP grenade
+//-----------------------------------------------------------------------------
+class CBaseEMPableGrenade : public CBaseGrenade
+{
+ DECLARE_CLASS( CBaseEMPableGrenade, CBaseGrenade );
+public:
+ DECLARE_PREDICTABLE();
+ DECLARE_NETWORKCLASS();
+
+#if !defined( CLIENT_DLL )
+ DECLARE_DATADESC();
+#endif
+
+ CBaseEMPableGrenade();
+
+ virtual bool CanTakeEMPDamage( void ) { return true; }
+ virtual bool TakeEMPDamage( float duration );
+
+ void FizzleThink( void );
+
+private:
+ CNetworkVar( float, m_flFizzleDuration );
+
+private:
+ CBaseEMPableGrenade( const CBaseEMPableGrenade & ); // not defined, not accessible
+
+};
+
+
+#endif // GRENADE_BASE_EMPABLE_H
diff --git a/game/shared/tf2/grenade_emp.cpp b/game/shared/tf2/grenade_emp.cpp
new file mode 100644
index 0000000..800c80f
--- /dev/null
+++ b/game/shared/tf2/grenade_emp.cpp
@@ -0,0 +1,343 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "basegrenade_shared.h"
+#include "engine/IEngineSound.h"
+#include "tf_shareddefs.h"
+#include "Sprite.h"
+#include "grenade_emp.h"
+#include "tf_gamerules.h"
+
+#if defined( CLIENT_DLL )
+
+#include "particles_simple.h"
+
+#else
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+int g_iEMPPulseEffectIndex = 0;
+
+// Damage CVars
+ConVar weapon_emp_grenade_duration( "weapon_emp_grenade_duration","5", FCVAR_REPLICATED, "Duration of the EMP grenade's effect." );
+ConVar weapon_emp_grenade_object_duration( "weapon_emp_grenade_object_duration","5", FCVAR_REPLICATED, "Duration of the EMP grenade's effect on objects." );
+ConVar weapon_emp_grenade_radius( "weapon_emp_grenade_radius","256", FCVAR_REPLICATED, "EMP grenade splash radius" );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( GrenadeEMP, DT_GrenadeEMP );
+
+BEGIN_NETWORK_TABLE( CGrenadeEMP, DT_GrenadeEMP )
+#if !defined( CLIENT_DLL )
+ SendPropEHandle( SENDINFO( m_hLiveSprite ) ),
+#else
+ RecvPropEHandle( RECVINFO( m_hLiveSprite ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CGrenadeEMP )
+ DEFINE_PRED_FIELD( m_hLiveSprite, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( grenade_emp, CGrenadeEMP );
+PRECACHE_REGISTER(grenade_emp);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CGrenadeEMP::CGrenadeEMP()
+{
+ SetPredictionEligible( true );
+
+#if defined( CLIENT_DLL )
+ m_ParticleEvent.Init( 100 );
+#else
+ UseClientSideAnimation();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::Precache( void )
+{
+ BaseClass::Precache( );
+
+ PrecacheModel( "models/weapons/w_grenade.mdl" );
+ PrecacheModel( "sprites/redglow1.vmt" );
+ g_iEMPPulseEffectIndex = PrecacheModel( "sprites/lgtning.spr" );
+
+ PrecacheScriptSound( "GrenadeEMP.Bounce" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::Spawn( void )
+{
+ Precache();
+
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
+ SetSolid( SOLID_BBOX );
+ //m_flGravity = 1.0;
+ SetFriction( 0.75 );
+ SetModel( "models/weapons/w_grenade.mdl" );
+ SetSize( Vector( -4, -4, -4), Vector(4, 4, 4) );
+ SetTouch( BounceTouch );
+ SetCollisionGroup( TFCOLLISION_GROUP_GRENADE );
+
+ m_flDetonateTime = gpGlobals->curtime + 4.0;
+ SetThink( TumbleThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ // Set my damages to the cvar values
+ SetDamage( weapon_emp_grenade_duration.GetFloat() );
+ SetDamageRadius( weapon_emp_grenade_radius.GetFloat() );
+
+ // Create a white light
+ CBasePlayer *player = ToBasePlayer( GetOwnerEntity() );
+ if ( player )
+ {
+ m_hLiveSprite = SPRITE_CREATE_PREDICTABLE( "sprites/chargeball2.vmt", GetLocalOrigin() + Vector(0,0,1), false );
+ if ( m_hLiveSprite )
+ {
+ m_hLiveSprite->SetOwnerEntity( player );
+ m_hLiveSprite->SetPlayerSimulated( player );
+ m_hLiveSprite->SetTransparency( kRenderGlow, 255, 255, 255, 128, kRenderFxNoDissipation );
+ m_hLiveSprite->SetBrightness( 255 );
+ m_hLiveSprite->SetScale( 0.15, 5.0f );
+ m_hLiveSprite->SetAttachment( this, 0 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::UpdateOnRemove( void )
+{
+ // Remove our live sprite
+ if ( m_hLiveSprite )
+ {
+ m_hLiveSprite->Remove( );
+ m_hLiveSprite = NULL;
+ }
+
+ // Chain at end to mimic destructor unwind order
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::Explode( trace_t *pTrace, int bitsDamageType )
+{
+#if !defined( CLIENT_DLL )
+ // While in scope, this will allow messages to pass through without being filtered.
+ CDisablePredictionFiltering dpf;
+
+ // Create EMP pulse effect
+ int iEmpRings = 4;
+ float fEmpDelay = 0.05f;
+ float delay = 0.0f;
+ float frac;
+ for ( int r = 0 ; r < iEmpRings; r++, delay += fEmpDelay )
+ {
+ frac = (float)( r )/(float)(iEmpRings - 1);
+
+ CBroadcastRecipientFilter filter;
+
+ // Since this doesn't fire on the client right now, ignore the culling of the local player
+ filter.SetIgnorePredictionCull( true );
+
+ te->BeamRingPoint( filter, delay,
+ GetAbsOrigin() + Vector(0,0,32) , // origin
+ 64.0f, // start radius
+ weapon_emp_grenade_radius.GetFloat() * 2, // end radius
+ g_iEMPPulseEffectIndex,
+ 0, // halo index
+ 0, // start frame
+ 2, // framerate
+ 0.3f, // life
+ 25.0, // width
+ 50, // spread
+ 2, // amplitude
+ 50 + ( 1-frac ) * 200,
+ 63,
+ 63 + 127 * frac,
+ 255 - frac * 127,
+ 20 );
+ }
+
+ ApplyRadiusEMPEffect( GetThrower(), GetAbsOrigin() + Vector(0,0,16) );
+ Remove( );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: EMP enemies around the grenade
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::ApplyRadiusEMPEffect( CBaseEntity *pOwner, const Vector& vecCenter )
+{
+ // Oh oh, owner is gone...
+ if ( !pOwner )
+ return;
+
+#if !defined( CLIENT_DLL )
+ CBaseEntity *pEntity = NULL;
+
+ for ( CEntitySphereQuery sphere( vecCenter, weapon_emp_grenade_radius.GetFloat() ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
+ {
+ // Ignore team members, and unaligned targets
+ if ( pOwner->InSameTeam( pEntity ) || pEntity->GetTeamNumber() == 0 )
+ continue;
+
+ if ( pEntity->IsSolidFlagSet( FSOLID_NOT_SOLID ) )
+ continue;
+
+ // Make sure it's not blocked by a shield or wall
+ trace_t tr;
+ if ( TFGameRules()->IsTraceBlockedByWorldOrShield( vecCenter, pEntity->WorldSpaceCenter(), this, DMG_PROBE, &tr ) )
+ continue;
+
+ if ( pEntity->CanBePoweredUp() )
+ {
+ // Is it an object?
+ if ( pEntity->Classify() == CLASS_MILITARY )
+ {
+ pEntity->AttemptToPowerup( POWERUP_EMP, weapon_emp_grenade_object_duration.GetFloat() );
+ }
+ else
+ {
+ pEntity->AttemptToPowerup( POWERUP_EMP, weapon_emp_grenade_duration.GetFloat() );
+ }
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allow shield parry's
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::BounceTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+ if ( !pOther->IsSolid() )
+ return;
+
+ BaseClass::BounceTouch( pOther );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Play a distinctive grenade bounce sound to warn nearby players
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::BounceSound( void )
+{
+ CPASAttenuationFilter filter( this, "GrenadeEMP.Bounce" );
+ filter.UsePredictionRules();
+ EmitSound( filter, entindex(), "GrenadeEMP.Bounce" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the amplitude for the screenshake
+//-----------------------------------------------------------------------------
+float CGrenadeEMP::GetShakeAmplitude( void )
+{
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a missile
+//-----------------------------------------------------------------------------
+CGrenadeEMP *CGrenadeEMP::Create( const Vector &vecOrigin, const Vector &vecForward, CBasePlayer *pOwner )
+{
+ CGrenadeEMP *pGrenade = (CGrenadeEMP*)CREATE_PREDICTED_ENTITY( "grenade_emp" );
+ if ( pGrenade )
+ {
+ UTIL_SetOrigin( pGrenade, vecOrigin );
+ pGrenade->SetOwnerEntity( pOwner );
+ pGrenade->Spawn();
+ pGrenade->SetPlayerSimulated( pOwner );
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ pGrenade->SetThrower( pOwner );
+
+ pGrenade->SetAbsVelocity( vecForward );
+
+ QAngle angles;
+ VectorAngles( vecForward, angles );
+ pGrenade->SetLocalAngles( angles );
+
+ pGrenade->SetLocalAngularVelocity( SHARED_RANDOMANGLE( -500, 500 ) );
+ }
+
+ return pGrenade;
+}
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ // Only think when sapping
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Trail smoke
+//-----------------------------------------------------------------------------
+void CGrenadeEMP::ClientThink( void )
+{
+return;
+
+ CSmartPtr<CSimpleEmitter> pEmitter = CSimpleEmitter::Create( "EMPGrenade::Effect" );
+ PMaterialHandle hSphereMaterial = pEmitter->GetPMaterial( "sprites/chargeball" );
+
+ // Add particles at the target.
+ float flCur = gpGlobals->frametime;
+ while ( m_ParticleEvent.NextEvent( flCur ) )
+ {
+ Vector vecOrigin = GetAbsOrigin() + RandomVector( -2,2 );
+ pEmitter->SetSortOrigin( vecOrigin );
+
+ SimpleParticle *pParticle = (SimpleParticle *) pEmitter->AddParticle( sizeof(SimpleParticle), hSphereMaterial, vecOrigin );
+ if ( pParticle == NULL )
+ return;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = random->RandomFloat( 0.1f, 0.3f );
+
+ pParticle->m_uchStartSize = random->RandomFloat(2,4);
+ pParticle->m_uchEndSize = pParticle->m_uchStartSize + 2;
+
+ pParticle->m_vecVelocity = vec3_origin;
+ pParticle->m_uchStartAlpha = 128;
+ pParticle->m_uchEndAlpha = 0;
+ pParticle->m_flRoll = random->RandomFloat( 180, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -1, 1 );
+
+ pParticle->m_uchColor[0] = 128;
+ pParticle->m_uchColor[1] = 128;
+ pParticle->m_uchColor[2] = 128;
+ }
+}
+
+int CGrenadeEMP::DrawModel( int flags )
+{
+ bool bret = BaseClass::DrawModel( flags );
+
+ return bret;
+}
+
+#endif
+
diff --git a/game/shared/tf2/grenade_emp.h b/game/shared/tf2/grenade_emp.h
new file mode 100644
index 0000000..3ee68a4
--- /dev/null
+++ b/game/shared/tf2/grenade_emp.h
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GRENADE_EMP_H
+#define GRENADE_EMP_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CSprite;
+
+#include "grenade_base_empable.h"
+
+#if defined( CLIENT_DLL )
+
+#define CGrenadeEMP C_GrenadeEMP
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: EMP grenade
+//-----------------------------------------------------------------------------
+class CGrenadeEMP : public CBaseEMPableGrenade
+{
+ DECLARE_CLASS( CGrenadeEMP, CBaseEMPableGrenade );
+public:
+ CGrenadeEMP();
+
+ DECLARE_PREDICTABLE();
+ DECLARE_NETWORKCLASS();
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+ virtual void UpdateOnRemove( void );
+ virtual void Explode( trace_t *pTrace, int bitsDamageType );
+ virtual void BounceTouch( CBaseEntity *pOther );
+ virtual void BounceSound( void );
+ virtual float GetShakeAmplitude( void );
+ virtual int GetDamageType() const { return DMG_BLAST; }
+
+ void ApplyRadiusEMPEffect( CBaseEntity *pOwner, const Vector& vecCenter );
+
+ static CGrenadeEMP *CGrenadeEMP::Create( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner );
+
+ // A derived class should return true here so that weapon sounds, etc, can
+ // apply the proper filter
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetThrower() &&
+ GetThrower() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void ClientThink( void );
+
+ TimedEvent m_ParticleEvent;
+
+ virtual int DrawModel( int flags );
+
+#endif
+
+private:
+ CNetworkHandle( CSprite, m_hLiveSprite );
+
+private:
+ CGrenadeEMP( const CGrenadeEMP & );
+};
+
+#endif // GRENADE_EMP_H
diff --git a/game/shared/tf2/grenade_limpetmine.cpp b/game/shared/tf2/grenade_limpetmine.cpp
new file mode 100644
index 0000000..098d626
--- /dev/null
+++ b/game/shared/tf2/grenade_limpetmine.cpp
@@ -0,0 +1,406 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "tf_basecombatweapon.h"
+#include "basegrenade_shared.h"
+#include "weapon_limpetmine.h"
+#include "engine/IEngineSound.h"
+#include "grenade_limpetmine.h"
+#include "tf_shareddefs.h"
+#include "IEffects.h"
+#include "player.h"
+#include "basetfvehicle.h"
+
+#define LIMPET_LIVE_TIME 0.5 // Time it takes before a limpet can be detonated after placement
+#define LIMPET_LIFETIME 120 // After this time, limpets fizzle naturally
+#define LIMPET_FIZZLE_DURATION 2.0f
+#define LIMPET_MINS Vector(-5, -5, 0)
+#define LIMPET_MAXS Vector( 5, 5, 10)
+
+// Damage CVars
+ConVar weapon_limpetmine_grenade_damage( "weapon_limpetmine_grenade_damage","0", FCVAR_NONE, "Limpet Mine's grenade maximum damage" );
+ConVar weapon_limpetmine_grenade_radius( "weapon_limpetmine_grenade_radius","0", FCVAR_NONE, "Limpet Mine's grenade splash radius" );
+
+// Global Savedata for friction modifier
+BEGIN_DATADESC( CLimpetMine )
+ // Function Pointers
+ DEFINE_THINKFUNC( LiveThink ),
+ DEFINE_ENTITYFUNC( StickyTouch ),
+ DEFINE_THINKFUNC( LimpetThink ),
+END_DATADESC()
+
+
+IMPLEMENT_SERVERCLASS_ST(CLimpetMine, DT_LimpetMine)
+ SendPropInt(SENDINFO(m_bLive), 1, SPROP_UNSIGNED ),
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( grenade_limpetmine, CLimpetMine );
+
+//-----------------------------------------------------------------------------
+// Static initializers:
+//-----------------------------------------------------------------------------
+CLimpetMine* CLimpetMine::allLimpets = NULL;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CLimpetMine::CLimpetMine( void )
+{
+ UseClientSideAnimation();
+
+ // ---------------------------------
+ // Add to linked list of limpets
+ // ---------------------------------
+ nextLimpet = CLimpetMine::allLimpets;
+ CLimpetMine::allLimpets = this;
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CLimpetMine::~CLimpetMine( void )
+{
+ // --------------------------------------
+ // Remove from linked list of limpets
+ // --------------------------------------
+ CLimpetMine *pLimpet = CLimpetMine::allLimpets;
+ if (pLimpet == this)
+ {
+ CLimpetMine::allLimpets = pLimpet->nextLimpet;
+ }
+ else
+ {
+ while (pLimpet)
+ {
+ if (pLimpet->nextLimpet == this)
+ {
+ pLimpet->nextLimpet = pLimpet->nextLimpet->nextLimpet;
+ break;
+ }
+ pLimpet = pLimpet->nextLimpet;
+ }
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLimpetMine::Precache( void )
+{
+ PrecacheModel( "models/projectiles/grenade_limpet.mdl" );
+ PrecacheScriptSound( "LimpetMine.Beep" );
+ PrecacheScriptSound( "LimpetMine.Fizzle" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLimpetMine::Spawn( void )
+{
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
+ SetSolid( SOLID_BBOX );
+ SetGravity( 1.0 );
+ SetFriction( 1.25 );
+ SetModel( "models/projectiles/grenade_limpet.mdl");
+ UTIL_SetSize( this, LIMPET_MINS, LIMPET_MAXS );
+ m_bLive = false;
+ m_bFizzleInit = false;
+ m_bEMPed = false;
+ SetThink( LiveThink );
+ SetNextThink( gpGlobals->curtime + LIMPET_LIVE_TIME );
+ SetTouch( StickyTouch );
+
+ // Causes these to collide with everything but NPCs and players
+ SetCollisionGroup( TFCOLLISION_GROUP_GRENADE );
+
+ AddFlag( FL_OBJECT );
+ // Prevent sentry guns detecting these.
+ AddFlag( FL_NOTARGET );
+
+ // Set my damages to the cvar values
+ SetDamage( weapon_limpetmine_grenade_damage.GetFloat() );
+ SetDamageRadius( weapon_limpetmine_grenade_radius.GetFloat() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this limpet mine can be detonated yet
+//-----------------------------------------------------------------------------
+bool CLimpetMine::IsLive( void )
+{
+ return m_bLive;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CLimpetMine::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ Assert( pCaller );
+
+ // USE_SET Means we're being asked to fizzle out and dielf
+ if ( useType == USE_SET )
+ {
+ if ( !m_bFizzleInit )
+ {
+ // Set the defuse - fizzle think
+ m_flFizzleDuration = gpGlobals->curtime + 0.3;
+ SetThink( LimpetThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ m_bFizzleInit = true;
+ }
+ }
+ else if ( IsLive() )
+ {
+ // Get the TF2 player that owns this limpet:
+ CBaseTFPlayer *pPlayer = NULL;
+ if ( m_hLauncher )
+ {
+ pPlayer = ToBaseTFPlayer( m_hLauncher->GetOwner() );
+ }
+
+ // Get the TF2 player that is calling this object, if any:
+ CBaseTFPlayer *pCallerPlayer = NULL;
+ if ( pCaller )
+ {
+ pCallerPlayer = ToBaseTFPlayer( pCaller );
+ }
+
+ // If the owning player is directly using the limpet, then pick it up:
+ if( pPlayer && pCallerPlayer && pPlayer->IsSameClass( pCallerPlayer ) )
+ {
+ if ( m_hLauncher )
+ {
+ pPlayer->GiveAmmo( 1, m_hLauncher->m_iPrimaryAmmoType );
+ m_hLauncher->DecrementLimpets();
+ }
+ UTIL_Remove( this );
+ }
+ else if ( pActivator && !pActivator->InSameTeam( this ) )
+ {
+ // only the owning player can detonate his own limpets, so return if this isn't the owner.
+ return;
+ }
+
+ // We're being detonated
+
+ // If are EMPed then we cannot be detonated.
+ else if ( !IsEMPed() )
+ {
+ // Beep and detonate soon afterwards
+ EmitSound( "LimpetMine.Beep" );
+
+ SetThink( Detonate );
+ SetNextThink( gpGlobals->curtime + 0.5f );
+
+ // Pretend I'm not live anymore so I don't get exploded again
+ m_bLive = false;
+
+ if ( m_hLauncher )
+ {
+ m_hLauncher->DecrementLimpets();
+ }
+
+ }
+
+
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CLimpetMine::TakeEMPDamage( float duration )
+{
+ if ( !m_bFizzleInit )
+ {
+ // Set the defuse - fizzle think
+ float flDuration = MIN( duration, LIMPET_FIZZLE_DURATION );
+ m_flFizzleDuration = gpGlobals->curtime + ( flDuration - 1.0f );
+ SetThink( LimpetThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ m_bFizzleInit = true;
+ m_bEMPed = true;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a limpet mine
+//-----------------------------------------------------------------------------
+CLimpetMine* CLimpetMine::Create( const Vector &vecOrigin, const Vector &vecForward, CBasePlayer *pOwner )
+{
+ CLimpetMine *pGrenade = (CLimpetMine*)CreateEntityByName("grenade_limpetmine");
+
+ pGrenade->Teleport( &vecOrigin, NULL, NULL );
+ pGrenade->Spawn();
+ pGrenade->SetOwnerEntity( pOwner );
+ pGrenade->SetThrower( pOwner );
+ pGrenade->SetAbsVelocity( vecForward );
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ return pGrenade;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Keep a pointer to the launcher (parent)
+//-----------------------------------------------------------------------------
+void CLimpetMine::SetLauncher( CWeaponLimpetmine *pLauncher )
+{
+ m_hLauncher = pLauncher;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Go Live
+//-----------------------------------------------------------------------------
+void CLimpetMine::LiveThink( void )
+{
+ m_bLive = true;
+
+ // Remove myself after a while
+ m_flFizzleDuration = gpGlobals->curtime + LIMPET_LIFETIME + 0.3;
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ SetThink( LimpetThink );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make the grenade stick to whatever it touches
+//-----------------------------------------------------------------------------
+void CLimpetMine::StickyTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+ if ( !pOther->IsSolid() )
+ return;
+
+ if ( !pOther->IsBSPModel() && !pOther->GetBaseAnimating() )
+ return;
+
+ BounceSound();
+ m_bStuckToTarget = false;
+
+ // Bounce off of shields
+ if ( pOther->GetCollisionGroup() == TFCOLLISION_GROUP_SHIELD )
+ {
+ // Move away from the shield...
+ // Fling it out a little extra along the plane normal
+ Vector vecNewVelocity;
+ Vector vecCenter;
+ AngleVectors( pOther->GetAbsAngles(), &vecCenter );
+ VectorMultiply( vecCenter, 400.0f, vecNewVelocity );
+ SetAbsVelocity( vecNewVelocity );
+ return;
+ }
+
+ // Only stick to non-moving entities
+ if ( !pOther->GetBaseAnimating() )
+ return;
+
+ // Don't stick to team members
+ if ( InSameTeam( pOther ) )
+ return;
+
+ // ROBIN: Removed stick to enemies for now
+ {
+ SetAbsVelocity( vec3_origin );
+ SetMoveType( MOVETYPE_NONE );
+ return;
+ }
+
+ m_bStuckToTarget = true;
+
+ // Orient to stick to the wall I just hit
+ trace_t tr;
+ Vector vecPrev = GetLocalOrigin() - (GetAbsVelocity() * 0.1);
+ UTIL_TraceLine( vecPrev, vecPrev + (GetAbsVelocity() * 2), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction != 1 )
+ {
+ // Orient the *up* axis to be along the plane normal
+ Vector perp( 1, 0, 0 );
+ Vector forward, right;
+ CrossProduct( perp, tr.plane.normal, forward );
+ if (forward.LengthSqr() < 0.1f)
+ {
+ perp.Init( 0, 1, 0 );
+ CrossProduct( perp, tr.plane.normal, forward );
+ }
+ VectorNormalize( forward );
+ CrossProduct( tr.plane.normal, forward, right );
+
+ VMatrix orientation( forward, right, tr.plane.normal );
+
+ QAngle angles;
+ MatrixToAngles( orientation, angles );
+ SetAbsAngles( angles );
+ }
+
+ SetAbsVelocity( vec3_origin );
+ SetMoveType( MOVETYPE_NONE );
+
+ // At this point, it shouldn't affect player movement
+ SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+ BounceSound();
+
+ SetParent( pOther );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Once the limpet's active, it starts running this
+//-----------------------------------------------------------------------------
+void CLimpetMine::LimpetThink( void )
+{
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ // If I'm not ready to fizzle yet, make sure my parent's still there.
+ if ( m_bStuckToTarget )
+ {
+ // Lost our parent?
+ if ( !GetMoveParent() )
+ {
+ m_bStuckToTarget = false;
+ // Fall to the ground
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+ SetTouch( StickyTouch );
+ }
+ }
+
+ float flDeltaTime = m_flFizzleDuration - gpGlobals->curtime;
+
+ // Not ready to fizzle yet?
+ if ( flDeltaTime > 0.3 )
+ return;
+
+ // Start fizzling
+ if ( flDeltaTime > 0.0f )
+ {
+ // Emit a fizzle sound
+ EmitSound( "LimpetMine.Fizzle" );
+
+ g_pEffects->Sparks( GetAbsOrigin() );
+
+ // Smoke.
+ UTIL_Smoke( GetAbsOrigin(), random->RandomInt( 1, 3), 10 );
+ }
+ else
+ {
+ // Done fizzling - no more sound.
+ StopSound( "LimpetMine.Fizzle" );
+ UTIL_Remove( this );
+
+ // Remove this limpet mine from the launcher deployment count.
+ if ( m_hLauncher )
+ {
+ m_hLauncher->DecrementLimpets();
+ }
+
+ return;
+ }
+}
diff --git a/game/shared/tf2/grenade_limpetmine.h b/game/shared/tf2/grenade_limpetmine.h
new file mode 100644
index 0000000..426df4b
--- /dev/null
+++ b/game/shared/tf2/grenade_limpetmine.h
@@ -0,0 +1,70 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GRENADE_LIMPETMINE_H
+#define GRENADE_LIMPETMINE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CWeaponLimpetmine;
+
+//=====================================================================================================
+// LIMPET MINE
+//=====================================================================================================
+class CLimpetMine : public CBaseGrenade
+{
+ DECLARE_CLASS( CLimpetMine, CBaseGrenade );
+
+public:
+ DECLARE_SERVERCLASS();
+ DECLARE_DATADESC();
+
+ CLimpetMine( void );
+ virtual ~CLimpetMine( void );
+
+ // Creation and Initialization
+ virtual void Spawn( void );
+ virtual void Precache( void );
+ static CLimpetMine* CLimpetMine::Create( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner );
+ virtual int GetDamageType() const { return DMG_BLAST; }
+ virtual bool CanBePoweredUp( void ) { return false; }
+
+ // Detonation
+ virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ bool IsLive( void );
+ virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_IMPULSE_USE; }
+
+ // EMP
+ virtual bool CanTakeEMPDamage() { return true; }
+ virtual bool TakeEMPDamage( float duration );
+ bool IsEMPed( void ) { return m_bEMPed; }
+
+ // Think and Touch
+ void LiveThink( void );
+ void StickyTouch( CBaseEntity *pOther );
+ void LimpetThink( void );
+
+ // Parent
+ void SetLauncher( CWeaponLimpetmine *pLauncher );
+
+public:
+ static CLimpetMine* allLimpets; // A linked list of all limpets
+ CLimpetMine* nextLimpet; // The next limpet in list of all limpets
+
+
+ CNetworkVar( bool, m_bLive ); // are we active?
+ bool m_bStuckToTarget; // If true, the limpet stuck to something when it went active
+ bool m_bEMPed; // have we been EMPed?
+ bool m_bFizzleInit; // initialize the fizzle (EMP) process
+ float m_flFizzleDuration; // fizzle duration
+
+ CHandle<CWeaponLimpetmine> m_hLauncher; // parent (weapon launched from)
+
+};
+
+#endif // GRENADE_LIMPETMINE_H
diff --git a/game/shared/tf2/grenade_objectsapper.cpp b/game/shared/tf2/grenade_objectsapper.cpp
new file mode 100644
index 0000000..5975d35
--- /dev/null
+++ b/game/shared/tf2/grenade_objectsapper.cpp
@@ -0,0 +1,211 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "player.h"
+#include "tf_obj.h"
+#include "basegrenade_shared.h"
+#include "grenade_objectsapper.h"
+#include "engine/IEngineSound.h"
+
+// Damage CVars
+static ConVar weapon_objectsapper_damage( "weapon_objectsapper_damage","50", FCVAR_NONE, "Damage done, per second, by the object sapper." );
+
+// Global Savedata for friction modifier
+BEGIN_DATADESC( CGrenadeObjectSapper )
+
+ // Function Pointers
+ DEFINE_THINKFUNC( SapperThink ),
+
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST(CGrenadeObjectSapper, DT_GrenadeObjectSapper)
+ SendPropInt(SENDINFO(m_bSapping), 1, SPROP_UNSIGNED ),
+END_SEND_TABLE();
+
+LINK_ENTITY_TO_CLASS( grenade_objectsapper, CGrenadeObjectSapper );
+PRECACHE_WEAPON_REGISTER(grenade_objectsapper);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeObjectSapper::Spawn( void )
+{
+ SetMoveType( MOVETYPE_NONE );
+ SetSolid( SOLID_BBOX );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+ SetGravity( 0.0 );
+ SetFriction( 1.0 );
+ SetModel( "models/sapper.mdl");
+ UTIL_SetSize(this, Vector( -8, -8, -8), Vector(8, 8, 8));
+ SetCollisionGroup( TFCOLLISION_GROUP_WEAPON );
+ m_takedamage = DAMAGE_NO;
+ m_iHealth = 50.0;
+ m_bSapping = false;
+
+ // Set my damages to the cvar values
+ SetDamage( weapon_objectsapper_damage.GetFloat() * 0.1 );
+ SetDamageRadius( 0 );
+
+ SetTouch( NULL );
+ SetThink( SapperThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ m_bArmed = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeObjectSapper::Precache( void )
+{
+ PrecacheModel( "models/sapper.mdl" );
+
+ PrecacheScriptSound( "GrenadeObjectSapper.Arming" );
+ PrecacheScriptSound( "GrenadeObjectSapper.RemoveSapper" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeObjectSapper::PlayArmingSound( void )
+{
+ EmitSound( "GrenadeObjectSapper.Arming" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : armed -
+//-----------------------------------------------------------------------------
+void CGrenadeObjectSapper::SetArmed( bool armed )
+{
+ bool ch = armed != m_bArmed;
+ m_bArmed = armed;
+
+ // Going armed
+ if ( ch && m_bArmed )
+ {
+ PlayArmingSound();
+ }
+
+ if ( m_bArmed )
+ {
+ RemoveEffects( EF_NODRAW );
+ }
+ else
+ {
+ AddEffects( EF_NODRAW );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CGrenadeObjectSapper::GetArmed( void ) const
+{
+ return m_bArmed;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sap the health from the object I'm attached to
+//-----------------------------------------------------------------------------
+void CGrenadeObjectSapper::SapperThink( void )
+{
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ // Not armed yet?
+ if ( !GetArmed() )
+ return;
+
+ // Remove myself if I'm armed, but don't have an object to sap
+ if ( !m_hTargetObject )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ m_bSapping = true;
+
+ // Damage our target (add DMG_CRUSH to prevent physics damage)
+ m_hTargetObject->TakeDamage( CTakeDamageInfo( this, GetThrower(), GetDamage(), GetDamageType() | DMG_CRUSH ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set our target object
+//-----------------------------------------------------------------------------
+void CGrenadeObjectSapper::SetTargetObject( CBaseObject *pObject )
+{
+ // Remove myself from any object I'm on
+ if ( m_hTargetObject != pObject )
+ {
+ if ( m_hTargetObject.Get() )
+ {
+ m_hTargetObject->RemoveSapper( this );
+ SetParent( NULL );
+ }
+
+ m_hTargetObject = pObject;
+
+ // Tell any object I've just been attached to
+ if ( m_hTargetObject )
+ {
+ m_hTargetObject->AddSapper( this );
+ SetParent( m_hTargetObject );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allow players to remove sappers from objects
+//-----------------------------------------------------------------------------
+void CGrenadeObjectSapper::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ // Only enemies remove the sapper
+ if ( !InSameTeam( pActivator ) )
+ {
+ // Enemy is grabbing me
+ EmitSound( "GrenadeObjectSapper.RemoveSapper" );
+ SetTargetObject( NULL );
+ UTIL_Remove( this );
+ }
+/*
+ ROBIN: Removed self-removal of sapper
+
+ else
+ {
+ // Ignore everyone except my owner
+ if ( pPlayer != m_hOwner )
+ return;
+ if ( pPlayer->GiveAmmo( 1, "Sappers") )
+ {
+ // Picked up, remove me
+ SetTargetObject( NULL );
+ UTIL_Remove( this );
+ }
+ }
+*/
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create an object sapper grenade
+//-----------------------------------------------------------------------------
+CGrenadeObjectSapper *CGrenadeObjectSapper::Create( const Vector &vecOrigin, const Vector &vecForward, CBasePlayer *pOwner, CBaseObject *pObject )
+{
+ CGrenadeObjectSapper *pGrenade = (CGrenadeObjectSapper*)CreateEntityByName("grenade_objectsapper");
+
+ UTIL_SetOrigin( pGrenade, vecOrigin );
+ pGrenade->Spawn();
+ pGrenade->SetThrower( pOwner );
+ pGrenade->SetAbsVelocity( vec3_origin );
+ QAngle angles;
+ VectorAngles( vecForward, angles );
+ angles.x -= 90;
+ pGrenade->SetLocalAngles( angles );
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ return pGrenade;
+}
diff --git a/game/shared/tf2/grenade_objectsapper.h b/game/shared/tf2/grenade_objectsapper.h
new file mode 100644
index 0000000..8984a21
--- /dev/null
+++ b/game/shared/tf2/grenade_objectsapper.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GRENADE_OBJECTSAPPER_H
+#define GRENADE_OBJECTSAPPER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CBaseObject;
+
+//-----------------------------------------------------------------------------
+// Purpose: Object sapper grenade
+//-----------------------------------------------------------------------------
+class CGrenadeObjectSapper : public CBaseGrenade
+{
+ DECLARE_CLASS( CGrenadeObjectSapper, CBaseGrenade );
+public:
+ DECLARE_DATADESC();
+ DECLARE_SERVERCLASS();
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+ virtual int GetDamageType() const { return DMG_BLAST; }
+ virtual void SapperThink( void );
+ void SetTargetObject( CBaseObject *pObject );
+
+ void SetArmed( bool armed );
+ bool GetArmed( void ) const;
+
+ void PlayArmingSound( void );
+
+ // Pickup
+ virtual int ObjectCaps( void ) { return FCAP_IMPULSE_USE; };
+ virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+ static CGrenadeObjectSapper *CGrenadeObjectSapper::Create( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner, CBaseObject *pObject );
+
+public:
+ CNetworkVar( bool, m_bSapping );
+ CHandle<CBaseObject> m_hTargetObject;
+
+ bool m_bArmed;
+};
+
+#endif // GRENADE_OBJECTSAPPER_H
diff --git a/game/shared/tf2/grenade_rocket.cpp b/game/shared/tf2/grenade_rocket.cpp
new file mode 100644
index 0000000..881ed74
--- /dev/null
+++ b/game/shared/tf2/grenade_rocket.cpp
@@ -0,0 +1,176 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "engine/IEngineSound.h"
+#include "grenade_rocket.h"
+
+extern short g_sModelIndexFireball;
+
+IMPLEMENT_SERVERCLASS_ST( CGrenadeRocket, DT_GrenadeRocket)
+END_SEND_TABLE()
+
+BEGIN_DATADESC( CGrenadeRocket )
+
+ DEFINE_FIELD( m_flDamage, FIELD_FLOAT ),
+
+ // Function Pointers
+ DEFINE_FUNCTION( MissileTouch ),
+ DEFINE_FUNCTION( FollowThink ),
+
+END_DATADESC()
+LINK_ENTITY_TO_CLASS( grenade_rocket, CGrenadeRocket );
+PRECACHE_REGISTER(grenade_rocket);
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CGrenadeRocket::CGrenadeRocket()
+{
+ m_pRealOwner = NULL;
+ m_hLockTarget = NULL;
+ UseClientSideAnimation();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeRocket::Precache( void )
+{
+ PrecacheModel( "models/weapons/w_missile.mdl" );
+
+ PrecacheScriptSound( "GrenadeRocket.FlyLoop" );
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeRocket::Spawn( void )
+{
+ Precache();
+
+ SetMoveType( MOVETYPE_FLY );
+ SetSolid( SOLID_BBOX );
+ SetModel( "models/weapons/w_missile.mdl" );
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+
+ SetCollisionGroup( TFCOLLISION_GROUP_WEAPON );
+ SetTouch( MissileTouch );
+
+ SetDamage( 50 );
+
+ // Forward!
+ Vector forward;
+ AngleVectors( GetLocalAngles(), &forward, NULL, NULL );
+ SetAbsVelocity( forward * ROCKET_VELOCITY );
+
+ EmitSound( "GrenadeRocket.FlyLoop" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeRocket::MissileTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+ if ( !pOther->IsSolid() )
+ return;
+
+ Vector vecAbsOrigin = GetAbsOrigin();
+ CPASFilter filter( vecAbsOrigin );
+ te->Explosion( filter, 0.0, &vecAbsOrigin, g_sModelIndexFireball, 2.0, 15, TE_EXPLFLAG_NONE, 100, m_flDamage );
+
+ StopSound( "GrenadeRocket.FlyLoop" );
+
+ // Don't apply explosive damage if it hit a shield of any kind...
+ bool bHittingShield = false;
+ if (pOther->GetCollisionGroup() == TFCOLLISION_GROUP_SHIELD)
+ {
+ bHittingShield = true;
+ }
+ else if ( pOther->IsPlayer() )
+ {
+ CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>(pOther);
+
+ trace_t tr;
+ float flDamage = m_flDamage;
+ bHittingShield = pPlayer->IsHittingShield( GetAbsVelocity(), &flDamage );
+ }
+
+ if (!bHittingShield)
+ {
+ RadiusDamage( CTakeDamageInfo( this, m_pRealOwner, m_flDamage, DMG_BLAST ), vecAbsOrigin, 100, CLASS_NONE, NULL );
+ }
+
+ UTIL_Remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make this rocket lock onto it's target and track it
+//-----------------------------------------------------------------------------
+void CGrenadeRocket::LockOnto( CBaseEntity *pTarget )
+{
+ m_hLockTarget = pTarget;
+ SetThink( FollowThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Try and turn towards the target point
+//-----------------------------------------------------------------------------
+void CGrenadeRocket::FollowThink( void )
+{
+ if ( m_hLockTarget == NULL )
+ return;
+
+ // Weave slightly drunkenly to target
+ Vector vecTarget = m_hLockTarget->GetAbsOrigin() - GetLocalOrigin();
+ VectorNormalize( vecTarget );
+
+ QAngle angles;
+ VectorAngles( vecTarget, angles );
+ SetLocalAngles( angles );
+
+ Vector vecVelocity = GetAbsVelocity();
+ float flSpeed = vecVelocity.Length();
+ vecVelocity = vecVelocity * 0.2 + vecTarget * flSpeed * 1.2;
+ // Clip to maxspeed
+ if ( vecVelocity.Length() > ROCKET_VELOCITY )
+ {
+ VectorNormalize( vecVelocity );
+ vecVelocity *= ROCKET_VELOCITY;
+ }
+ SetAbsVelocity( vecVelocity );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a missile
+//-----------------------------------------------------------------------------
+CGrenadeRocket *CGrenadeRocket::Create( const Vector &vecOrigin, const Vector &vecForward, edict_t *pentOwner = NULL, CBaseEntity *pRealOwner = NULL )
+{
+ CGrenadeRocket *pRocket = (CGrenadeRocket *)CreateEntityByName("grenade_rocket" );
+
+ UTIL_SetOrigin( pRocket, vecOrigin );
+ QAngle angles;
+ VectorAngles( vecForward, angles );
+ pRocket->SetLocalAngles( angles );
+ pRocket->Spawn();
+ pRocket->SetOwnerEntity( Instance( pentOwner ) );
+ pRocket->m_pRealOwner = pRealOwner;
+
+ if (pentOwner)
+ {
+ CBaseEntity *pOwnerEnt = GetContainingEntity( pentOwner );
+ pRocket->ChangeTeam( pOwnerEnt->GetTeamNumber() );
+ }
+
+ return pRocket;
+}
diff --git a/game/shared/tf2/grenade_rocket.h b/game/shared/tf2/grenade_rocket.h
new file mode 100644
index 0000000..e3f7d56
--- /dev/null
+++ b/game/shared/tf2/grenade_rocket.h
@@ -0,0 +1,60 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef GRENADE_ROCKET_H
+#define GRENADE_ROCKET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define ROCKET_VELOCITY 1000
+
+//====================================================================================
+// Purpose: ROCKET LAUNCHER SENTRYGUN'S ROCKETS
+//====================================================================================
+class CGrenadeRocket : public CBaseAnimating
+{
+ DECLARE_CLASS( CGrenadeRocket, CBaseAnimating );
+public:
+
+ DECLARE_DATADESC();
+ DECLARE_SERVERCLASS();
+
+ CGrenadeRocket();
+
+ void Spawn( void );
+ void Precache( void );
+ void MissileTouch( CBaseEntity *pOther );
+ void LockOnto( CBaseEntity *pTarget );
+ void FollowThink( void );
+
+ // Damage accessors.
+ virtual float GetDamage(void)
+ {
+ return m_flDamage;
+ }
+
+ virtual void SetDamage(float flDamage)
+ {
+ m_flDamage = flDamage;
+ }
+
+ virtual int GetDamageType() const
+ {
+ return DMG_BLAST;
+ }
+
+ static CGrenadeRocket *CGrenadeRocket::Create( const Vector &vecOrigin, const Vector &vecAngles, edict_t *pentOwner, CBaseEntity *pRealOwner );
+
+public:
+ EHANDLE m_hLockTarget;
+ EHANDLE m_hOwner;
+ EHANDLE m_pRealOwner;
+ float m_flDamage;
+};
+
+#endif // GRENADE_ROCKET_H
diff --git a/game/shared/tf2/grenade_stickybomb.cpp b/game/shared/tf2/grenade_stickybomb.cpp
new file mode 100644
index 0000000..dfadbdc
--- /dev/null
+++ b/game/shared/tf2/grenade_stickybomb.cpp
@@ -0,0 +1,129 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Sticky bombs thrown by the recon
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "player.h"
+#include "basegrenade_shared.h"
+#include "tf_shareddefs.h"
+#include "Sprite.h"
+
+
+// Damage CVars
+ConVar grenade_stickybomb_damage( "grenade_stickybomb_damage","0", 0, "Recon's stickybomb maximum damage" );
+ConVar grenade_stickybomb_radius( "grenade_stickybomb_radius","0", 0, "Recon's stickybomb splash radius" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CGrenadeStickyBomb : public CBaseGrenade
+{
+ DECLARE_CLASS( CGrenadeStickyBomb, CBaseGrenade );
+public:
+ CGrenadeStickyBomb();
+
+ DECLARE_DATADESC();
+
+ void Spawn( void );
+ void Precache( void );
+ void SetTimer( float timer );
+ void StickyTouch( CBaseEntity *pOther );
+ virtual void Explode( trace_t *pTrace, int bitsDamageType );
+ virtual int GetDamageType() const { return DMG_BLAST; }
+
+private:
+ CSprite *m_pLiveSprite;
+};
+
+// Global Savedata for friction modifier
+BEGIN_DATADESC( CGrenadeStickyBomb )
+
+ // Function Pointers
+ DEFINE_ENTITYFUNC( StickyTouch ),
+
+END_DATADESC()
+
+
+LINK_ENTITY_TO_CLASS( grenade_stickybomb, CGrenadeStickyBomb );
+PRECACHE_WEAPON_REGISTER(grenade_stickybomb);
+
+CGrenadeStickyBomb::CGrenadeStickyBomb()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeStickyBomb::Precache( void )
+{
+ PrecacheModel( "models/weapons/w_grenade.mdl" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeStickyBomb::Spawn( void )
+{
+ Precache();
+
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE );
+ SetSolid( SOLID_BBOX );
+ SetGravity( 0.6 );
+ SetFriction( 1.0 );
+ SetModel( "models/weapons/w_grenade.mdl");
+ UTIL_SetSize(this, Vector( -4, -4, -4), Vector(4, 4, 4));
+ SetTouch( StickyTouch );
+ SetCollisionGroup( TFCOLLISION_GROUP_WEAPON );
+
+ // Create a red light
+ m_pLiveSprite = CSprite::SpriteCreate( "sprites/redglow1.vmt", GetLocalOrigin(), false );
+ m_pLiveSprite->SetTransparency( kRenderGlow, 255, 200, 200, 255, kRenderFxNoDissipation );
+ m_pLiveSprite->SetBrightness( 255 );
+ m_pLiveSprite->SetScale( 0.3 );
+ m_pLiveSprite->SetAttachment( this, 0 );
+
+ // Set my damages to the cvar values
+ SetDamage( grenade_stickybomb_damage.GetFloat() );
+ SetDamageRadius( grenade_stickybomb_radius.GetFloat() );
+
+ SetTimer( 2.0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CGrenadeStickyBomb::SetTimer( float timer )
+{
+ SetThink( Detonate );
+ SetNextThink( gpGlobals->curtime + timer );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Make the grenade stick to whatever it touches
+//-----------------------------------------------------------------------------
+void CGrenadeStickyBomb::StickyTouch( CBaseEntity *pOther )
+{
+ if ( pOther->IsBSPModel() == false )
+ return;
+
+ BounceSound();
+ SetAbsVelocity( vec3_origin );
+ SetMoveType( MOVETYPE_NONE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove my glow when I'm removed
+//-----------------------------------------------------------------------------
+void CGrenadeStickyBomb::Explode( trace_t *pTrace, int bitsDamageType )
+{
+ if ( m_pLiveSprite )
+ {
+ UTIL_Remove( m_pLiveSprite );
+ m_pLiveSprite = NULL;
+ }
+
+ BaseClass::Explode( pTrace, bitsDamageType );
+}
diff --git a/game/shared/tf2/ihasbuildpoints.h b/game/shared/tf2/ihasbuildpoints.h
new file mode 100644
index 0000000..bb24d74
--- /dev/null
+++ b/game/shared/tf2/ihasbuildpoints.h
@@ -0,0 +1,60 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef IHASBUILDPOINTS_H
+#define IHASBUILDPOINTS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CBaseObject;
+
+// Derive from this interface if your entity can have objects placed on build points on it
+class IHasBuildPoints
+{
+public:
+ // Tell me how many build points you have
+ virtual int GetNumBuildPoints( void ) const = 0;
+
+ // Give me the origin & angles of the specified build point
+ virtual bool GetBuildPoint( int iPoint, Vector &vecOrigin, QAngle &vecAngles ) = 0;
+
+ // If the build point wants to parent built objects to an attachment point on the entity,
+ // it'll return a value >= 1 here specifying which attachment to sit on.
+ virtual int GetBuildPointAttachmentIndex( int iPoint ) const = 0;
+
+ // Can I build the specified object on the specified build point?
+ virtual bool CanBuildObjectOnBuildPoint( int iPoint, int iObjectType ) = 0;
+
+ // I've finished building the specified object on the specified build point
+ virtual void SetObjectOnBuildPoint( int iPoint, CBaseObject *pObject ) = 0;
+
+ // Get the number of objects build on this entity
+ virtual int GetNumObjectsOnMe( void ) = 0;
+
+ // Get the first object that's built on me
+ virtual CBaseEntity *GetFirstObjectOnMe( void ) = 0;
+
+ // Get the first object of type, return NULL if no such type available
+ virtual CBaseObject *GetObjectOfTypeOnMe( int iObjectType ) = 0;
+
+ // Remove all objects built on me
+ virtual void RemoveAllObjects( void ) = 0;
+
+ // Return the maximum distance that this entity's build points can be snapped to
+ virtual float GetMaxSnapDistance( int iPoint ) = 0;
+
+ // Return true if it's possible that build points on this entity may move in local space (i.e. due to animation)
+ virtual bool ShouldCheckForMovement( void ) = 0;
+
+ // I've finished building the specified object on the specified build point
+ virtual int FindObjectOnBuildPoint( CBaseObject *pObject ) = 0;
+
+ // Returns an exit point for a vehicle built on a build point...
+ virtual void GetExitPoint( CBaseEntity *pPlayer, int iPoint, Vector *pAbsOrigin, QAngle *pAbsAngles ) = 0;
+};
+
+#endif // IHASBUILDPOINTS_H
diff --git a/game/shared/tf2/plasmaprojectile.cpp b/game/shared/tf2/plasmaprojectile.cpp
new file mode 100644
index 0000000..d2d5ce4
--- /dev/null
+++ b/game/shared/tf2/plasmaprojectile.cpp
@@ -0,0 +1,843 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "plasmaprojectile.h"
+//#include "smoke_trail.h"
+#include "basecombatweapon_shared.h"
+#include "tf_shareddefs.h"
+#if !defined( CLIENT_DLL )
+#include "tf_shield.h"
+#else
+#include "c_tracer.h"
+#include "hud.h"
+#include "view.h"
+#include "c_te_effect_dispatch.h"
+#endif
+#include "IEffects.h"
+//#include "tf_player.h"
+#include "basetfplayer_shared.h"
+#include "engine/IEngineSound.h"
+#include "worldsize.h"
+#include "tf_gamerules.h"
+#include "ammodef.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+extern ConVar tf_knockdowntime;
+
+#define PLASMA_LIFETIME 2.0
+
+// Time intervals at which we should simulate plasma projectiles
+#define PLASMA_SIM_DELTA 0.01
+#define PLASMA_VELOCITY_SQR (PLASMA_VELOCITY*PLASMA_VELOCITY)
+
+#if defined( CLIENT_DLL )
+ ConVar shot_width( "shot_width","8", 0, "Shot" );
+ ConVar shot_length( "shot_length","140", 0, "Shot" );
+ ConVar shot_head_size( "shot_head_size","6", 0, "Shot" );
+#endif
+
+#if !defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose: PLASMA PROJECTILE
+//-----------------------------------------------------------------------------
+BEGIN_DATADESC( CBasePlasmaProjectile )
+
+ DEFINE_FIELD( m_flDamage, FIELD_FLOAT ),
+
+ // Function Pointers
+ DEFINE_ENTITYFUNC( MissileTouch ),
+
+END_DATADESC()
+#endif
+
+BEGIN_NETWORK_TABLE_NOBASE( CPlasmaProjectileShared, DT_PlasmaProjectileShared )
+#if !defined( CLIENT_DLL )
+ // These are parameters that are used to generate the entire motion
+ SendPropVector(SENDINFO(m_vecSpawnPosition), 0, SPROP_COORD),
+ SendPropVector(SENDINFO(m_vTracerDir), 0, SPROP_NOSCALE), //SPROP_NORMAL),
+ SendPropTime(SENDINFO(m_flSpawnTime)),
+ SendPropTime(SENDINFO(m_flDeathTime)),
+ SendPropFloat(SENDINFO(m_flSpawnSpeed), 0, SPROP_NOSCALE),
+#else
+ RecvPropVector(RECVINFO(m_vecSpawnPosition)),
+ RecvPropVector(RECVINFO(m_vTracerDir)),
+ RecvPropTime(RECVINFO(m_flSpawnTime)),
+ RecvPropTime(RECVINFO(m_flDeathTime)),
+ RecvPropFloat(RECVINFO(m_flSpawnSpeed)),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA_NO_BASE( CPlasmaProjectileShared )
+
+ DEFINE_PRED_FIELD_TOL( m_vecSpawnPosition, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.125f ),
+ DEFINE_PRED_FIELD_TOL( m_vTracerDir, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.01f ),
+ DEFINE_PRED_FIELD( m_flSpawnTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flDeathTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flSpawnSpeed, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PositionHistory_t )
+
+ DEFINE_FIELD( m_Position, FIELD_VECTOR ),
+ DEFINE_FIELD( m_Time, FIELD_FLOAT ),
+
+END_PREDICTION_DATA()
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BasePlasmaProjectile, DT_BasePlasmaProjectile)
+
+BEGIN_NETWORK_TABLE( CBasePlasmaProjectile, DT_BasePlasmaProjectile )
+#if !defined( CLIENT_DLL )
+ SendPropDataTable(SENDINFO_DT(m_Shared), &REFERENCE_SEND_TABLE(DT_PlasmaProjectileShared)),
+
+ SendPropExclude( "DT_BaseEntity", "m_vecVelocity" ),
+ SendPropExclude( "DT_BaseEntity", "m_vecAbsOrigin" ),
+
+ //SendPropVector(SENDINFO(m_vecGunOriginOffset), 0, SPROP_COORD),
+
+#else
+ RecvPropDataTable(RECVINFO_DT(m_Shared), 0, &REFERENCE_RECV_TABLE(DT_PlasmaProjectileShared)),
+
+ //RecvPropVector(RECVINFO(m_vecGunOriginOffset)),
+#endif
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( base_plasmaprojectile, CBasePlasmaProjectile );
+PRECACHE_REGISTER(base_plasmaprojectile);
+
+BEGIN_PREDICTION_DATA( CBasePlasmaProjectile )
+
+ DEFINE_PRED_TYPEDESCRIPTION( m_Shared, CPlasmaProjectileShared ),
+
+ DEFINE_PRED_FIELD( m_vecAbsOrigin, FIELD_VECTOR, FTYPEDESC_PRIVATE | FTYPEDESC_OVERRIDE ),
+ DEFINE_PRED_FIELD( m_vecVelocity, FIELD_VECTOR, FTYPEDESC_PRIVATE | FTYPEDESC_OVERRIDE ),
+
+ DEFINE_FIELD( m_flMaxRange, FIELD_FLOAT ),
+
+ // Predicted, but not in networking stream
+ DEFINE_PRED_TYPEDESCRIPTION( m_pPreviousPositions[0], PositionHistory_t ),
+ DEFINE_PRED_TYPEDESCRIPTION( m_pPreviousPositions[1], PositionHistory_t ),
+ DEFINE_PRED_TYPEDESCRIPTION( m_pPreviousPositions[2], PositionHistory_t ),
+ DEFINE_PRED_TYPEDESCRIPTION( m_pPreviousPositions[3], PositionHistory_t ),
+ DEFINE_PRED_TYPEDESCRIPTION( m_pPreviousPositions[4], PositionHistory_t ),
+
+END_PREDICTION_DATA()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBasePlasmaProjectile::CBasePlasmaProjectile()
+{
+#if defined( CLIENT_DLL )
+ m_pHeadParticle = NULL;
+ m_pTrailParticle = NULL;
+ m_pParticleMgr = NULL;
+#endif
+
+ SetPredictionEligible( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBasePlasmaProjectile::~CBasePlasmaProjectile()
+{
+#if defined( CLIENT_DLL )
+ if( m_pParticleMgr )
+ {
+ m_pParticleMgr->RemoveEffect( &m_ParticleEffect );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::Precache( void )
+{
+ SetCollisionGroup( TFCOLLISION_GROUP_WEAPON );
+
+ PrecacheScriptSound( "BasePlasmaProjectile.ShieldBlock" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::Spawn( void )
+{
+ Precache();
+
+ SetSolid( SOLID_BBOX );
+ SetSize( vec3_origin, vec3_origin );
+ SetCollisionGroup( TFCOLLISION_GROUP_WEAPON );
+ SetTouch( MissileTouch );
+ m_DamageType = DMG_ENERGYBEAM;
+ SetMoveType( MOVETYPE_CUSTOM );
+ m_flDamage = 0;
+ // SetMaxRange( 0 );
+ SetExplosive( 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::Activate( void )
+{
+ BaseClass::Activate();
+
+#if defined( CLIENT_DLL )
+ if ( IsClientCreated() && !m_pParticleMgr )
+ {
+ Start(ParticleMgr(), NULL);
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::SetDamage( float flDamage )
+{
+ m_flDamage = flDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CBasePlasmaProjectile::GetDamage( void )
+{
+ return m_flDamage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::SetMaxRange( float flRange )
+{
+ m_flMaxRange = flRange;
+
+ // If we have a max range, calculate death time based upon velocity
+ if ( m_flMaxRange )
+ {
+ float flSpeed = GetAbsVelocity().Length();
+ Assert( flSpeed );
+ m_Shared.SetDeathTime( m_Shared.GetSpawnTime() + (flRange / flSpeed) );
+ }
+ else
+ {
+ m_Shared.SetDeathTime( m_Shared.GetSpawnTime() + PLASMA_LIFETIME );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the radius of the explosion created when this shot impacts
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::SetExplosive( float flRadius )
+{
+ m_flExplosiveRadius = flRadius;
+}
+
+//-----------------------------------------------------------------------------
+// Perform custom physics on this dude (when we're in ballistic mode)
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::PerformCustomPhysics( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity )
+{
+#ifdef CLIENT_DLL
+ RecalculatePositions( pNewPosition, pNewVelocity, pNewAngles, pNewAngVelocity );
+#else
+ // Simulate next position
+ m_Shared.ComputePosition( gpGlobals->curtime, pNewPosition, pNewVelocity, pNewAngles, pNewAngVelocity );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+// tr -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBasePlasmaProjectile::ProjectileHitShield( CBaseEntity *pOther, trace_t& tr )
+{
+ if ( !pOther )
+ return false;
+
+ if ( !pOther->IsPlayer() )
+ return false;
+#if !defined( CLIENT_DLL )
+ CBaseTFPlayer* pPlayer = static_cast<CBaseTFPlayer*>(pOther);
+ float flDamage = GetDamage();
+ if ( !pPlayer->IsHittingShield( GetAbsVelocity(), &flDamage ) )
+ return false;
+#else
+ return false;
+#endif
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+// tr -
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::HandleShieldImpact( CBaseEntity *pOther, trace_t& tr )
+{
+ // Block
+ EmitSound( "BasePlasmaProjectile.ShieldBlock" );
+
+ // Remove the particle, and make a particle shower
+ g_pEffects->EnergySplash( tr.endpos, tr.plane.normal, ( m_flExplosiveRadius != 0 ) );
+
+ Remove( );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::MissileTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+ if ( !pOther->IsSolid() )
+ return;
+
+ // Create a plasma effect
+ trace_t tr;
+ Vector velDir = GetAbsVelocity();
+ VectorNormalize( velDir );
+ Vector vecSpot = GetLocalOrigin() - velDir * 32;
+
+ // First, just clip to the box
+ Ray_t ray;
+ ray.Init( vecSpot, vecSpot + velDir * 64 );
+ enginetrace->ClipRayToEntity( ray, MASK_SHOT, pOther, &tr );
+
+ // Create the appropriate impact
+ bool bHurtTarget = ( !InSameTeam( pOther ) && pOther->m_takedamage != DAMAGE_NO );
+ WeaponImpact( &tr, velDir, bHurtTarget, pOther, GetDamageType() );
+
+#if !defined( CLIENT_DLL )
+ CBaseEntity *pOwner = m_hOwner;
+
+ // Do damage (unless I'm explosive, in which case I'll do damage later)
+ if ( m_flDamage && !m_flExplosiveRadius )
+ {
+ ClearMultiDamage();
+ // Assume it's a projectile, so use its velocity instead
+ Vector vecDamageOrigin = GetAbsVelocity();
+ VectorNormalize( vecDamageOrigin );
+ vecDamageOrigin = GetAbsOrigin() - (vecDamageOrigin * 32);
+ CTakeDamageInfo info( this, pOwner, m_flDamage, m_DamageType );
+ CalculateBulletDamageForce( &info, GetAmmoDef()->Index("MediumRound"), GetAbsVelocity(), vecDamageOrigin );
+ pOther->DispatchTraceAttack( info, velDir, &tr );
+ ApplyMultiDamage();
+ }
+#endif
+
+ Detonate();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Plasma projectiles return their owner as their scorer
+//-----------------------------------------------------------------------------
+CBasePlayer *CBasePlasmaProjectile::GetScorer( void )
+{
+ return ToBasePlayer( m_hOwner );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Explode and die
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::Detonate( void )
+{
+#if !defined( CLIENT_DLL )
+ // Should I explode?
+ if ( m_flExplosiveRadius )
+ {
+ RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), m_flDamage, m_DamageType | DMG_BLAST ), GetAbsOrigin(), m_flExplosiveRadius, CLASS_NONE, NULL );
+ }
+#endif
+ Remove( );
+}
+
+#if defined( CLIENT_DLL )
+
+//-----------------------------------------------------------------------------
+// Add the position to the history
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::AddPositionToHistory( const Vector& org, float flSimTime )
+{
+ // Store the particle position history
+ // Push the others down the stack
+ for ( int i = MAX_HISTORY-1; i >= 1; i-- )
+ {
+ m_pPreviousPositions[i].m_Position = m_pPreviousPositions[i-1].m_Position;
+ m_pPreviousPositions[i].m_Time = m_pPreviousPositions[i-1].m_Time;
+ }
+
+ m_pPreviousPositions[0].m_Position = org;
+ m_pPreviousPositions[0].m_Time = flSimTime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : org -
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::ResetPositionHistories( const Vector& org )
+{
+ for ( int i = 0; i < MAX_HISTORY; i++ )
+ {
+ m_pPreviousPositions[ i ].m_Position = org; //; - (m_Shared.TracerDir() * 48 * i);;
+ m_pPreviousPositions[ i ].m_Time = gpGlobals->curtime;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::OnDataChanged(DataUpdateType_t updateType)
+{
+ BaseClass::OnDataChanged(updateType);
+
+ if ( updateType != DATA_UPDATE_CREATED )
+ return;
+
+ if ( !m_pParticleMgr )
+ {
+ Start(ParticleMgr(), NULL);
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::RecalculatePositions( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity )
+{
+ // Recalculate all points?
+ float flSimTime;
+ if ( !m_pPreviousPositions[0].m_Time )
+ {
+ flSimTime = m_Shared.GetSpawnTime();
+ }
+ else
+ {
+ flSimTime = gpGlobals->curtime;
+ }
+
+ // Simulate the points
+ for ( int i = 0; i < MAX_HISTORY; i++ )
+ {
+ if ( flSimTime < m_Shared.GetSpawnTime() )
+ {
+ flSimTime = m_Shared.GetSpawnTime();
+ }
+
+ Vector vecVelocity, vNewOrigin;
+ QAngle vecAngles, vecAngularVelocity;
+ // Only fill out the data with the most recent sim
+ if ( i == 0 )
+ {
+ m_Shared.ComputePosition( flSimTime, &vNewOrigin, &vecVelocity, pNewAngles, pNewAngVelocity );
+ *pNewPosition = vNewOrigin;
+ *pNewVelocity =vecVelocity;
+ }
+ else
+ {
+ m_Shared.ComputePosition( flSimTime, &vNewOrigin, &vecVelocity, &vecAngles, &vecAngularVelocity );
+ }
+ AddPositionToHistory( vNewOrigin, flSimTime );
+
+ // As we slow down, simulate slower
+ float flSpeed = vecVelocity.LengthSqr();
+ if ( flSpeed )
+ {
+ flSimTime -= PLASMA_SIM_DELTA * (PLASMA_VELOCITY_SQR / flSpeed);
+ }
+ else
+ {
+ flSimTime -= PLASMA_SIM_DELTA;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::ClientThink( void )
+{
+ BaseClass::ClientThink();
+
+ // Don't mess with origin if it's being forward simulated on the client
+ if ( GetPredictable() || IsClientCreated() )
+ return;
+
+ Assert( !GetMoveParent() );
+
+ Vector pNewPosition, pNewVelocity;
+ QAngle pNewAngles, pNewAngVelocity;
+ RecalculatePositions( &pNewPosition, &pNewVelocity, &pNewAngles, &pNewAngVelocity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : isbeingremoved -
+// *predicted -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBasePlasmaProjectile::OnPredictedEntityRemove( bool isbeingremoved, C_BaseEntity *predicted )
+{
+ BaseClass::OnPredictedEntityRemove( isbeingremoved, predicted );
+
+ CBasePlasmaProjectile *bpp = dynamic_cast< CBasePlasmaProjectile * >( predicted );
+ if ( !bpp )
+ {
+ // Hrm, we didn't link up to correct type!!!
+ Assert( 0 );
+ // Delete right away since it's fucked up
+ return true;
+ }
+
+ memcpy( m_pPreviousPositions, bpp->m_pPreviousPositions, sizeof( m_pPreviousPositions ) );
+
+ m_vecGunOriginOffset = bpp->m_vecGunOriginOffset;
+
+ // Don't delete right away
+ return true; // isbeingremoved;
+}
+
+#define REMAP_BLEND_TIME 0.5f
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : slot -
+// curtime -
+// outpos -
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::RemapPosition( Vector &vecStart, float curtime, Vector& outpos )
+{
+ outpos = vecStart;
+ if ( curtime > m_Shared.GetSpawnTime() + REMAP_BLEND_TIME )
+ return;
+
+ float frac = ( curtime - m_Shared.GetSpawnTime() ) / REMAP_BLEND_TIME;
+ frac = 1.0f - clamp( frac, 0.0f, 1.0f );
+
+ Vector scaledOffset;
+ VectorScale( m_vecGunOriginOffset, frac, scaledOffset );
+
+ outpos += scaledOffset;
+}
+
+#define TIME_TILL_MAX_LENGTH 1.0
+
+//-----------------------------------------------------------------------------
+// Purpose: Update state + render
+//-----------------------------------------------------------------------------
+bool CBasePlasmaProjectile::SimulateAndRender(Particle *pInParticle, ParticleDraw *pDraw, float &sortKey)
+{
+ if ( IsDormantPredictable() )
+ return true;
+
+ if ( GetMoveType() == MOVETYPE_NONE )
+ return true;
+
+ // Update the particle position
+ pInParticle->m_Pos = GetAbsOrigin();
+
+ // Add our blended offset
+ if ( gpGlobals->curtime < m_Shared.GetSpawnTime() + REMAP_BLEND_TIME )
+ {
+ float frac = ( gpGlobals->curtime - m_Shared.GetSpawnTime() ) / REMAP_BLEND_TIME;
+ frac = 1.0f - clamp( frac, 0.0f, 1.0f );
+ Vector scaledOffset;
+ VectorScale( m_vecGunOriginOffset, frac, scaledOffset );
+ pInParticle->m_Pos += scaledOffset;
+ }
+
+ float timeDelta = pDraw->GetTimeDelta();
+
+ // Render the head particle
+ if ( pInParticle == m_pHeadParticle )
+ {
+ SimpleParticle *pParticle = (SimpleParticle *) pInParticle;
+ pParticle->m_flLifetime += timeDelta;
+
+ // Render
+ Vector tPos, vecOrigin;
+ RemapPosition( m_pPreviousPositions[MAX_HISTORY-1].m_Position, m_pPreviousPositions[MAX_HISTORY-1].m_Time, vecOrigin );
+
+ TransformParticle( ParticleMgr()->GetModelView(), vecOrigin, tPos );
+ sortKey = (int) tPos.z;
+
+ //Render it
+ RenderParticle_ColorSizeAngle(
+ pDraw,
+ tPos,
+ UpdateColor( pParticle, timeDelta ),
+ UpdateAlpha( pParticle, timeDelta ) * GetAlphaDistanceFade( tPos, 16, 64 ),
+ UpdateScale( pParticle, timeDelta ),
+ UpdateRoll( pParticle, timeDelta ) );
+
+ /*
+ if ( m_flNextSparkEffect < gpGlobals->curtime )
+ {
+ // Drop sparks?
+ if ( GetTeamNumber() == TEAM_HUMANS )
+ {
+ g_pEffects->Sparks( pInParticle->m_Pos, 1, 3 );
+ }
+ else
+ {
+ g_pEffects->EnergySplash( pInParticle->m_Pos, vec3_origin );
+ }
+ m_flNextSparkEffect = gpGlobals->curtime + RandomFloat( 0.5, 2 );
+ }
+ */
+
+ return true;
+ }
+
+ // Render the trail
+ TrailParticle *pParticle = (TrailParticle *) pInParticle;
+ pParticle->m_flLifetime += timeDelta;
+ Vector vecScreenStart, vecScreenDelta;
+ sortKey = pParticle->m_Pos.z;
+
+ // NOTE: We need to do everything in screen space
+ float flFragmentLength = (MAX_HISTORY > 1) ? 1.0 / (float)(MAX_HISTORY-1) : 1.0;
+
+ for ( int i = 0; i < (MAX_HISTORY-1); i++ )
+ {
+ Vector vecWorldStart, vecWorldEnd, vecScreenEnd;
+ float flStartV, flEndV;
+
+ // Did we just appear?
+ if ( m_pPreviousPositions[i].m_Time == 0 )
+ continue;
+
+ RemapPosition( m_pPreviousPositions[i+1].m_Position, m_pPreviousPositions[i+1].m_Time, vecWorldStart );
+ RemapPosition( m_pPreviousPositions[i].m_Position, m_pPreviousPositions[i].m_Time, vecWorldEnd );
+
+ // Texture wrapping
+ flStartV = (flFragmentLength * (i+1));
+ flEndV = (flFragmentLength * i);
+
+ TransformParticle( ParticleMgr()->GetModelView(), vecWorldStart, vecScreenStart );
+ TransformParticle( ParticleMgr()->GetModelView(), vecWorldEnd, vecScreenEnd );
+ Vector vecScreenDelta = (vecScreenEnd - vecScreenStart);
+ if ( vecScreenDelta == vec3_origin )
+ continue;
+
+ /*
+ Vector vecForward, vecRight;
+ AngleVectors( MainViewAngles(), &vecForward, &vecRight, NULL );
+ Vector vecWorldDelta = ( vecWorldEnd - vecWorldStart );
+ VectorNormalize( vecWorldDelta );
+ float flDot = fabs(DotProduct( vecWorldDelta, vecForward ));
+ if ( flDot > 0.99 )
+ {
+ // Remap alpha
+ pParticle->m_flColor[3] = 1.0 - MIN( 1.0, RemapVal( flDot, 0.99, 1.0, 0, 1 ) );
+ }
+ */
+
+ // See if we should fade
+ float color[4];
+ Color32ToFloat4( color, pParticle->m_color );
+ Tracer_Draw( pDraw, vecScreenStart, vecScreenDelta, pParticle->m_flWidth, color, flStartV, flEndV );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs)
+{
+ m_pParticleMgr = pParticleMgr;
+ m_pParticleMgr->AddEffect( &m_ParticleEffect, this );
+
+ PMaterialHandle HeadMaterial, TrailMaterial;
+
+ // Load the projectile material
+ if ( GetTeamNumber() == TEAM_HUMANS )
+ {
+ HeadMaterial = m_ParticleEffect.FindOrAddMaterial( "effects/human_tracers/human_sparksprite_A1" );
+ TrailMaterial = m_ParticleEffect.FindOrAddMaterial( "effects/human_tracers/human_sparktracer_A_" );
+ }
+ else
+ {
+ HeadMaterial = m_ParticleEffect.FindOrAddMaterial( "effects/alien_tracers/alien_pbsprite_A1" );
+ TrailMaterial = m_ParticleEffect.FindOrAddMaterial( "effects/alien_tracers/alien_pbtracer_A_" );
+ }
+
+ // Create the head & trail
+ m_pHeadParticle = (SimpleParticle *)m_ParticleEffect.AddParticle(sizeof(SimpleParticle), HeadMaterial );
+ m_pTrailParticle = (TrailParticle *)m_ParticleEffect.AddParticle(sizeof(TrailParticle), TrailMaterial );
+ if ( !m_pHeadParticle || !m_pTrailParticle )
+ return;
+
+ // 3rd person particles are larger
+ bool bFirst = (GetOwnerEntity() == C_BasePlayer::GetLocalPlayer());
+
+ m_pHeadParticle->m_Pos = GetRenderOrigin();
+ m_pHeadParticle->m_uchColor[0] = 255;
+ m_pHeadParticle->m_uchColor[1] = 255;
+ m_pHeadParticle->m_uchColor[2] = 255;
+ if ( bFirst )
+ {
+ m_pHeadParticle->m_uchStartSize = 6;
+ }
+ else
+ {
+ m_pHeadParticle->m_uchStartSize = shot_head_size.GetInt();
+ }
+ m_pHeadParticle->m_uchEndSize = m_pHeadParticle->m_uchStartSize;
+ m_pHeadParticle->m_uchStartAlpha = 255;
+ m_pHeadParticle->m_uchEndAlpha = 255;
+ m_pHeadParticle->m_flRoll = 0;
+ m_pHeadParticle->m_flRollDelta = 10;
+ m_pHeadParticle->m_iFlags = 0;
+
+ m_pTrailParticle->m_flLifetime = 0;
+ m_pTrailParticle->m_Pos = GetRenderOrigin();
+ if ( bFirst )
+ {
+ m_pTrailParticle->m_flWidth = 25;
+ m_pTrailParticle->m_flLength = 140;
+ }
+ else
+ {
+ m_pTrailParticle->m_flWidth = shot_width.GetFloat();
+ m_pTrailParticle->m_flLength = shot_length.GetFloat();
+ }
+ Color32Init( m_pTrailParticle->m_color, 255, 255, 255, 255 );
+
+ m_flNextSparkEffect = gpGlobals->curtime + RandomFloat( 0.05, 0.4 );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup this projectile's starting values
+//-----------------------------------------------------------------------------
+void CBasePlasmaProjectile::SetupProjectile( const Vector &vecOrigin, const Vector &vecForward, int damageType, CBaseEntity *pOwner )
+{
+ UTIL_SetOrigin( this, vecOrigin );
+
+ QAngle angles;
+ VectorAngles( vecForward, angles );
+ SetLocalAngles( angles );
+
+ SetOwnerEntity( pOwner );
+ Spawn();
+
+ float flMySpeed = PLASMA_VELOCITY;// + RandomFloat( -500, 500 );
+ SetAbsVelocity( vecForward * flMySpeed );
+ m_DamageType = damageType;
+ m_Shared.Init( vecOrigin, vecForward, flMySpeed );
+#ifdef CLIENT_DLL
+ ResetPositionHistories( GetAbsOrigin() );
+#endif
+ m_Shared.SetSpawnTime( gpGlobals->curtime );
+
+ // Set my team
+ ChangeTeam( pOwner->GetTeamNumber() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a missile
+//-----------------------------------------------------------------------------
+CBasePlasmaProjectile *CBasePlasmaProjectile::Create( const Vector &vecOrigin, const Vector &vecForward, int damageType, CBaseEntity *pOwner = NULL )
+{
+ CBasePlasmaProjectile *pMissile = (CBasePlasmaProjectile*)CreateEntityByName("base_plasmaprojectile");
+ pMissile->SetupProjectile( vecOrigin, vecForward, damageType, pOwner );
+
+ return pMissile;
+}
+
+CBasePlasmaProjectile *CBasePlasmaProjectile::CreatePredicted( const Vector &vecOrigin, const Vector &vecForward, const Vector& gunOffset, int damageType, CBasePlayer *pOwner )
+{
+ CBasePlasmaProjectile *pMissile = (CBasePlasmaProjectile*)CREATE_PREDICTED_ENTITY("base_plasmaprojectile");
+ if ( pMissile )
+ {
+ pMissile->SetOwnerEntity( pOwner );
+ pMissile->SetPlayerSimulated( pOwner );
+ pMissile->SetupProjectile( vecOrigin, vecForward, damageType, pOwner );
+ pMissile->m_vecGunOriginOffset = gunOffset;
+ }
+
+ return pMissile;
+}
+
+//===============================================================================================================
+// Power Projectile
+//===============================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose: Create a power projectile
+//-----------------------------------------------------------------------------
+CPowerPlasmaProjectile *CPowerPlasmaProjectile::Create( const Vector &vecOrigin, const Vector &vecForward, int damageType, CBaseEntity *pOwner = NULL )
+{
+ CPowerPlasmaProjectile *pMissile = (CPowerPlasmaProjectile*)CreateEntityByName("powerplasmaprojectile");
+ pMissile->SetupProjectile( vecOrigin, vecForward, damageType, pOwner );
+ pMissile->SetPower( 1.0 );
+
+ return pMissile;
+}
+
+CPowerPlasmaProjectile *CPowerPlasmaProjectile::CreatePredicted( const Vector &vecOrigin, const Vector &vecForward, const Vector& gunOffset, int damageType, CBasePlayer *pOwner )
+{
+ CPowerPlasmaProjectile *pMissile = (CPowerPlasmaProjectile*)CREATE_PREDICTED_ENTITY("powerplasmaprojectile");
+ if ( pMissile )
+ {
+ pMissile->SetOwnerEntity( pOwner );
+ pMissile->SetPlayerSimulated( pOwner );
+ pMissile->SetupProjectile( vecOrigin, vecForward, damageType, pOwner );
+ pMissile->SetPower( 1.0 );
+ pMissile->m_vecGunOriginOffset = gunOffset;
+ }
+
+ return pMissile;
+}
+
+CPowerPlasmaProjectile::CPowerPlasmaProjectile( void )
+{
+ m_flPower = 0;
+ SetPredictionEligible( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Factor power into size
+//-----------------------------------------------------------------------------
+float CPowerPlasmaProjectile::GetSize( void )
+{
+ return ( 2 * (m_flPower * 2));
+}
+
+IMPLEMENT_NETWORKCLASS_ALIASED( PowerPlasmaProjectile, DT_PowerPlasmaProjectile);
+
+BEGIN_NETWORK_TABLE( CPowerPlasmaProjectile, DT_PowerPlasmaProjectile)
+#if !defined( CLIENT_DLL )
+ SendPropFloat( SENDINFO( m_flPower ), 7, SPROP_ROUNDDOWN, 1.0f, 10.0 ),
+#else
+ RecvPropFloat(RECVINFO(m_flPower)),
+#endif
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( powerplasmaprojectile, CPowerPlasmaProjectile );
+PRECACHE_REGISTER(powerplasmaprojectile);
+
+BEGIN_PREDICTION_DATA( CPowerPlasmaProjectile )
+
+ DEFINE_PRED_FIELD_TOL( m_flPower, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.05f ),
+
+END_PREDICTION_DATA()
diff --git a/game/shared/tf2/plasmaprojectile.h b/game/shared/tf2/plasmaprojectile.h
new file mode 100644
index 0000000..13427e6
--- /dev/null
+++ b/game/shared/tf2/plasmaprojectile.h
@@ -0,0 +1,245 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PLASMAPROJECTILE_H
+#define PLASMAPROJECTILE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "predictable_entity.h"
+
+#include "baseparticleentity.h"
+#include "plasmaprojectile_shared.h"
+
+#if !defined( CLIENT_DLL )
+#include "iscorer.h"
+#else
+#include "particle_prototype.h"
+#include "particles_simple.h"
+#include "particle_util.h"
+#include "c_baseplayer.h"
+#include "fx_sparks.h"
+#endif
+
+#if defined( CLIENT_DLL )
+#define CBasePlasmaProjectile C_BasePlasmaProjectile
+#endif
+
+#define MAX_HISTORY 5
+#define GUIDED_FADE_TIME 0.25f
+#define GUIDED_WIDTH 3
+
+struct PositionHistory_t
+{
+ DECLARE_PREDICTABLE();
+
+ Vector m_Position;
+ float m_Time;
+};
+
+// ------------------------------------------------------------------------ //
+// CBasePlasmaProjectile
+// ------------------------------------------------------------------------ //
+class CBasePlasmaProjectile : public CBaseParticleEntity
+#if !defined( CLIENT_DLL )
+, public IScorer
+#endif
+{
+ DECLARE_CLASS( CBasePlasmaProjectile, CBaseParticleEntity );
+public:
+ CBasePlasmaProjectile();
+ ~CBasePlasmaProjectile();
+
+ DECLARE_PREDICTABLE();
+ DECLARE_NETWORKCLASS();
+
+#if !defined( CLIENT_DLL )
+ DECLARE_DATADESC();
+#endif
+
+ virtual bool ProjectileHitShield( CBaseEntity *pOther, trace_t& tr );
+ virtual void HandleShieldImpact( CBaseEntity *pOther, trace_t& tr );
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+ virtual void Activate( void );
+
+ virtual void MissileTouch( CBaseEntity *pOther );
+ virtual float GetDamage( void );
+ virtual void SetDamage( float flDamage );
+ virtual void SetMaxRange( float flRange );
+ virtual void SetExplosive( float flRadius );
+ virtual void PerformCustomPhysics( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity );
+
+ // Purpose: Returns the type of damage that this entity inflicts.
+ int GetDamageType() const
+ {
+ return m_DamageType;
+ }
+
+ virtual float GetSize( void ) { return 6.0; };
+
+ // FIXME!!!! Override the think of the baseparticle Think functions
+ virtual void Think( void ) { CBaseEntity::Think(); }
+
+ void SetupProjectile( const Vector &vecOrigin, const Vector &vecForward, int damageType, CBaseEntity *pOwner = NULL );
+ static CBasePlasmaProjectile *Create( const Vector &vecOrigin, const Vector &vecForward, int damageType, CBaseEntity *pOwner );
+ static CBasePlasmaProjectile *CreatePredicted( const Vector &vecOrigin, const Vector &vecForward, const Vector& gunOffset, int damageType, CBasePlayer *pOwner );
+
+ void RecalculatePositions( Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity );
+
+// IScorer
+public:
+ // Return the entity that should receive the score
+ virtual CBasePlayer *GetScorer( void );
+ // Return the entity that should get assistance credit
+ virtual CBasePlayer *GetAssistant( void ) { return NULL; };
+
+protected:
+ void Detonate( void );
+
+ // A derived class should return true here so that weapon sounds, etc, can
+ // apply the proper filter
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwnerEntity() &&
+ GetOwnerEntity() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ virtual void OnDataChanged(DataUpdateType_t updateType);
+ virtual void Start(CParticleMgr *pParticleMgr, IPrototypeArgAccess *pArgs);
+ virtual bool SimulateAndRender(Particle *pParticle, ParticleDraw *pDraw, float &sortKey);
+
+ // Add the position to the history
+ void AddPositionToHistory( const Vector& org, float flSimTime );
+ void ResetPositionHistories( const Vector& org );
+ // Adjustments for shots straight out of local player's eyes
+ void RemapPosition( Vector &vecStart, float curtime, Vector& outpos );
+
+ // Scale
+ virtual float UpdateScale( SimpleParticle *pParticle, float timeDelta )
+ {
+ return (float)pParticle->m_uchStartSize + RandomInt( -2,2 );
+ }
+
+ // Alpha
+ virtual float UpdateAlpha( SimpleParticle *pParticle, float timeDelta )
+ {
+ return (pParticle->m_uchStartAlpha + RandomInt( -50, 0 ) ) / 255.0f;
+ }
+ virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta )
+ {
+ pParticle->m_flRoll += pParticle->m_flRollDelta * timeDelta;
+
+ return pParticle->m_flRoll;
+ }
+ virtual Vector UpdateColor( SimpleParticle *pParticle, float timeDelta )
+ {
+ static Vector cColor;
+
+ cColor[0] = pParticle->m_uchColor[0] / 255.0f;
+ cColor[1] = pParticle->m_uchColor[1] / 255.0f;
+ cColor[2] = pParticle->m_uchColor[2] / 255.0f;
+
+ return cColor;
+ }
+
+ // Should this object cast shadows?
+ virtual ShadowType_t ShadowCastType() { return SHADOWS_NONE; }
+
+ virtual void ClientThink( void );
+ virtual bool OnPredictedEntityRemove( bool isbeingremoved, C_BaseEntity *predicted );
+
+protected:
+ SimpleParticle *m_pHeadParticle;
+ TrailParticle *m_pTrailParticle;
+ CParticleMgr *m_pParticleMgr;
+ float m_flNextSparkEffect;
+#endif
+public:
+ EHANDLE m_hOwner;
+
+protected:
+ CNetworkVarEmbedded( CPlasmaProjectileShared, m_Shared );
+
+ Vector m_vecGunOriginOffset;
+
+ CNetworkVar( float, m_flPower );
+
+ // Explosive radius
+ float m_flExplosiveRadius;
+
+ // Maximum range
+ float m_flMaxRange;
+
+ float m_flDamage;
+ int m_DamageType;
+
+ Vector m_vecTargetOffset;
+
+ PositionHistory_t m_pPreviousPositions[MAX_HISTORY];
+
+private:
+ CBasePlasmaProjectile( const CBasePlasmaProjectile & );
+};
+
+#if defined( CLIENT_DLL )
+#define CPowerPlasmaProjectile C_PowerPlasmaProjectile
+#endif
+
+// ------------------------------------------------------------------------ //
+// Plasma projectile that has a concept of variable power
+// ------------------------------------------------------------------------ //
+class CPowerPlasmaProjectile : public CBasePlasmaProjectile
+{
+ DECLARE_CLASS( CPowerPlasmaProjectile, CBasePlasmaProjectile );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CPowerPlasmaProjectile();
+
+ void SetPower( float flPower ) { m_flPower = flPower; };
+ static CPowerPlasmaProjectile* Create( const Vector &vecOrigin, const Vector &vecForward, int damageType, CBaseEntity *pOwner );
+ static CPowerPlasmaProjectile* CreatePredicted( const Vector &vecOrigin, const Vector &vecForward, const Vector& gunOffset, int damageType, CBasePlayer *pOwner );
+
+ virtual float GetSize( void );
+
+ // A derived class should return true here so that weapon sounds, etc, can
+ // apply the proper filter
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwnerEntity() &&
+ GetOwnerEntity() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+
+private:
+ CPowerPlasmaProjectile( const CPowerPlasmaProjectile & );
+
+};
+
+#endif // PLASMAPROJECTILE_H
diff --git a/game/shared/tf2/plasmaprojectile_shared.cpp b/game/shared/tf2/plasmaprojectile_shared.cpp
new file mode 100644
index 0000000..8bbe622
--- /dev/null
+++ b/game/shared/tf2/plasmaprojectile_shared.cpp
@@ -0,0 +1,72 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "plasmaprojectile_shared.h"
+
+#define PLASMA_LIFETIME 2.0
+
+ConVar plasma_gravity( "plasma_gravity","1000", FCVAR_REPLICATED, "Plasma gravity" );
+ConVar plasma_drag( "plasma_drag","2", FCVAR_REPLICATED, "Plasma drag" );
+
+
+//-----------------------------------------------------------------------------
+// Setup state needed to perform the physics computation
+//-----------------------------------------------------------------------------
+void CPlasmaProjectileShared::Init( const Vector &vecStart, const Vector &vecDir, float flSpawnSpeed )
+{
+ m_vecSpawnPosition = vecStart;
+ m_vTracerDir = vecDir;
+ m_flSpawnSpeed = flSpawnSpeed;
+}
+
+void CPlasmaProjectileShared::SetSpawnTime( float flSpawnTime )
+{
+ m_flSpawnTime = flSpawnTime;
+}
+
+void CPlasmaProjectileShared::SetDeathTime( float flDeathTime )
+{
+ m_flDeathTime = flDeathTime;
+}
+
+
+//-----------------------------------------------------------------------------
+// Perform custom physics on this dude (when we're in ballistic mode)
+//-----------------------------------------------------------------------------
+void CPlasmaProjectileShared::ComputePosition( float flTime, Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity )
+{
+ float flLifeTime = flTime - m_flSpawnTime;
+ if (flLifeTime < 0)
+ return;
+
+ // Travel ballistically until we run out of juice..
+ if (flTime <= m_flDeathTime)
+ {
+ VectorMultiply( m_vTracerDir, m_flSpawnSpeed, *pNewVelocity );
+ VectorMA( m_vecSpawnPosition, flLifeTime, *pNewVelocity, *pNewPosition );
+ }
+ else
+ {
+ VectorMultiply( m_vTracerDir, m_flSpawnSpeed, *pNewVelocity );
+ VectorMA( m_vecSpawnPosition, m_flDeathTime - m_flSpawnTime, *pNewVelocity, *pNewPosition );
+
+ // Ran out of juice... fall!
+ float flFallTime = flTime - m_flDeathTime;
+
+ float flDragFactor = exp( -plasma_drag.GetFloat() * flFallTime );
+ *pNewVelocity *= flDragFactor;
+
+ float flDist = (m_flSpawnSpeed / plasma_drag.GetFloat()) * ( 1.0f - flDragFactor );
+ VectorMA( *pNewPosition, flDist, m_vTracerDir, *pNewPosition );
+
+ // Add in the effects of gravity!
+ pNewVelocity->z -= flFallTime * plasma_gravity.GetFloat();
+ pNewPosition->z -= 0.5f * plasma_gravity.GetFloat() * flFallTime * flFallTime;
+ }
+}
+
+
diff --git a/game/shared/tf2/plasmaprojectile_shared.h b/game/shared/tf2/plasmaprojectile_shared.h
new file mode 100644
index 0000000..2b112b8
--- /dev/null
+++ b/game/shared/tf2/plasmaprojectile_shared.h
@@ -0,0 +1,48 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PLASMAPROJECTILE_SHARED_H
+#define PLASMAPROJECTILE_SHARED_H
+
+#ifndef _WIN32
+#pragma once
+#endif
+
+#include "predictable_entity.h"
+#include "server_class.h"
+#include "client_class.h"
+#include "mathlib/vector.h"
+
+class CPlasmaProjectileShared
+{
+public:
+ DECLARE_PREDICTABLE();
+ DECLARE_NETWORKCLASS_NOBASE();
+ DECLARE_CLASS_NOBASE( CPlasmaProjectileShared );
+ DECLARE_EMBEDDED_NETWORKVAR();
+
+public:
+ void Init( const Vector &vecStart, const Vector &vecDir, float flSpawnSpeed );
+ float GetSpawnTime() const { return m_flSpawnTime; }
+ void SetSpawnTime( float flSpawnTime );
+ void SetDeathTime( float flDeathTime );
+ float GetDeathTime() const { return m_flDeathTime; }
+
+ void ComputePosition( float flTime, Vector *pNewPosition, Vector *pNewVelocity, QAngle *pNewAngles, QAngle *pNewAngVelocity );
+
+ const Vector &TracerDir() { return m_vTracerDir.Get(); }
+
+private:
+ CNetworkVector( m_vTracerDir );
+ CNetworkVector( m_vecSpawnPosition );
+ CNetworkVar( float, m_flSpawnTime );
+ CNetworkVar( float, m_flSpawnSpeed );
+ CNetworkVar( float, m_flDeathTime );
+};
+
+
+#endif // PLASMAPROJECTILE_SHARED_H
diff --git a/game/shared/tf2/techtree.cpp b/game/shared/tf2/techtree.cpp
new file mode 100644
index 0000000..e1f732a
--- /dev/null
+++ b/game/shared/tf2/techtree.cpp
@@ -0,0 +1,1173 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Shared interface to the tech tree & individual technologies
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+
+#ifndef CLIENT_DLL
+#include "tf_player.h"
+#include "tf_team.h"
+#include "info_customtech.h"
+#endif
+#include "techtree.h"
+
+bool ParseTechnologyFile( CUtlVector< CBaseTechnology* > &pTechnologyList, IFileSystem* pFileSystem, int nTeamNumber, char *sFileName );
+
+// Color codes for resources
+rescolor sResourceColor = { 64, 255, 64 };
+
+// Prototype names for resources
+char sResourceName[] = "Jojierium";
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseTechnology::CBaseTechnology( void )
+{
+ m_nTechLevel = 0;
+ ZeroPreferences();
+ SetAvailable( false );
+ SetActive( false );
+ SetPreferred( false );
+ SetVoters( 0 );
+ SetCost( 0 );
+ SetDirty( false );
+ m_fResourceLevel = 0;
+ memset( m_ClassResults, 0, sizeof( m_ClassResults ) );
+ memset( m_pszName, 0, sizeof( m_pszName ) );
+ memset( m_pszPrintName, 0, sizeof( m_pszPrintName ) );
+ memset( m_pszDescription, 0, sizeof( m_pszDescription ) );
+ memset( m_pszTeamSoundFile, 0, sizeof( m_pszTeamSoundFile ) );
+ memset( m_apszContainedTechs, 0, sizeof( m_apszContainedTechs ) );
+ memset( m_pContainedTechs, 0, sizeof(m_pContainedTechs) );
+ memset( m_apszDependentTechs, 0, sizeof( m_apszDependentTechs ) );
+ memset( m_pDependentTechs, 0, sizeof(m_pDependentTechs) );
+ m_iContainedTechs = 0;
+ m_iDependentTechs = 0;
+ m_iTeamSound = 0;
+ m_bGoalTechnology = false;
+ m_bClassUpgrade = false;
+ m_bVehicle = false;
+ m_bTechLevelUpgrade = false;
+ m_bResourceTech = false;
+
+ memset( m_szTextureName, 0, sizeof( m_szTextureName ) );
+ m_nTextureID = 0;
+
+ memset( m_szButtonName, 0, sizeof( m_szButtonName ) );
+
+ m_nNumWeaponAssociations = 0;
+ memset( m_rgszWeaponAssociation, 0, sizeof( m_rgszWeaponAssociation ) );
+
+ SetHidden( false );
+ ResetHintsGiven();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseTechnology::~CBaseTechnology( void )
+{
+}
+
+static bool NameStartsWith( const char *name, const char *prefix )
+{
+ if ( !name || !name[ 0 ] || !prefix || !prefix[ 0 ] )
+ return false;
+
+ if ( !strnicmp( name, prefix, strlen( prefix ) ) )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the technology name
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetName( const char *pName )
+{
+ Q_strncpy( m_pszName, pName, sizeof(m_pszName) );
+
+ // Determine special information about this technology
+ m_bClassUpgrade = NameStartsWith( pName, "class_" );
+ m_bVehicle = NameStartsWith( pName, "vehicle_" );
+ m_bTechLevelUpgrade = NameStartsWith( pName, "tech_level_" );
+ // HACK: Assume global techs relate to resources for now
+ m_bResourceTech = NameStartsWith( pName, "g_" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the technology print name
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetPrintName( const char *pName )
+{
+ Q_strncpy( m_pszPrintName, pName, sizeof(m_pszPrintName) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the technology description
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetDescription( const char *pDesc )
+{
+ Q_strncpy( m_pszDescription, pDesc, sizeof(m_pszDescription) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pName -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetButtonName( const char *pName )
+{
+ Q_strncpy( m_szButtonName, pName, sizeof(m_szButtonName) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the technology level
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetLevel( int iLevel )
+{
+ m_nTechLevel = iLevel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *texture -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetTextureName( const char *texture )
+{
+ Q_strncpy( m_szTextureName, texture, sizeof( m_szTextureName ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : id -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetTextureId( int id )
+{
+ m_nTextureID = id;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the technologies resource costs
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetCost( float fResourceCost )
+{
+ m_fResourceCost = fResourceCost;
+
+ RecalculateOverallLevel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a specific class's sound
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetClassResultSound( int iClass, const char *pSound )
+{
+ if (!pSound)
+ return;
+
+ Assert( iClass >= 0 && iClass < TFCLASS_CLASS_COUNT );
+
+ // Class of 0 is the team's sound file
+ if ( iClass == 0 )
+ {
+ Q_strncpy( m_pszTeamSoundFile, pSound, sizeof(m_pszTeamSoundFile) );
+ }
+ else
+ {
+ m_ClassResults[iClass].bClassTouched = true;
+ Q_strncpy( m_ClassResults[iClass].pszSoundFile, pSound, sizeof(m_ClassResults[iClass].pszSoundFile) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : associate -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetClassResultAssociateWeapons( int iClass, bool associate )
+{
+ Assert( iClass >= 0 && iClass < TFCLASS_CLASS_COUNT );
+
+ m_ClassResults[iClass].m_bAssociateWeaponsForClass = associate;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a specific class's precached sound
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetClassResultSound( int iClass, int iSound )
+{
+ Assert( iClass >= 0 && iClass < TFCLASS_CLASS_COUNT );
+
+ // Class of 0 is the team's sound file
+ if ( iClass == 0 )
+ {
+ m_iTeamSound = iSound;
+ }
+ else
+ {
+ m_ClassResults[iClass].iSound = iSound;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set a specific class's description
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetClassResultDescription( int iClass, const char *pDesc )
+{
+ if (!pDesc)
+ return;
+
+ Assert( iClass > 0 && iClass < TFCLASS_CLASS_COUNT );
+ m_ClassResults[iClass].bClassTouched = true;
+
+ Q_strncpy( m_ClassResults[iClass].pszDescription, pDesc, sizeof(m_ClassResults[iClass].pszDescription) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a technology contained within this one
+//-----------------------------------------------------------------------------
+void CBaseTechnology::AddContainedTechnology( const char *pszTech )
+{
+ Q_strncpy( m_apszContainedTechs[ m_iContainedTechs ], pszTech, sizeof(m_apszContainedTechs[0]) );
+ m_iContainedTechs++;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a technology dependency within this one
+//-----------------------------------------------------------------------------
+void CBaseTechnology::AddDependentTechnology( const char *pszTech )
+{
+ Q_strncpy( m_apszDependentTechs[ m_iDependentTechs ], pszTech, sizeof(m_apszDependentTechs[0]) );
+ m_iDependentTechs++;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if this technology affects the specified class
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::AffectsClass( int iClass )
+{
+ // If this technology directly affects this class, return true
+ if ( m_ClassResults[ iClass ].bClassTouched )
+ return true;
+
+ // If not, do any of our contained techs affect the specified class
+ for (int i = 0; i < m_iContainedTechs; i++ )
+ {
+ if ( m_pContainedTechs[i] )
+ {
+ if ( m_pContainedTechs[i]->AffectsClass( iClass ) )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::IsClassUpgrade( void )
+{
+ return m_bClassUpgrade;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::IsVehicle( void )
+{
+ return m_bVehicle;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::IsResourceTech( void )
+{
+ return m_bResourceTech;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::IsTechLevelUpgrade( void )
+{
+ return m_bTechLevelUpgrade;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the level to which this technology belongs
+// Output : int
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetLevel( void )
+{
+ return m_nTechLevel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Cost of technology in resource specified
+//-----------------------------------------------------------------------------
+float CBaseTechnology::GetResourceCost( void )
+{
+ return m_fResourceCost;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieves the current amount of resources spent on the technology
+//-----------------------------------------------------------------------------
+float CBaseTechnology::GetResourceLevel( void )
+{
+ return m_fResourceLevel;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Sets a resource level to an amount
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetResourceLevel( float flResourceLevel )
+{
+ if ( m_fResourceLevel == flResourceLevel )
+ return;
+
+ m_fResourceLevel = flResourceLevel;
+
+ // Update my level & watchers
+ RecalculateOverallLevel();
+ UpdateWatchers();
+
+ // Force me to be resent to clients
+ SetDirty( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Increase the level of the specified resource spent on this technology
+// Output : Returns true if the technology's had enough resources to be bought
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::IncreaseResourceLevel( float flResourcesToSpend )
+{
+ SetResourceLevel( m_fResourceLevel + flResourcesToSpend );
+
+ // Have my costs been met?
+ if ( GetResourceLevel() < GetResourceCost() )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Figure out my overall owned percentage
+//-----------------------------------------------------------------------------
+void CBaseTechnology::RecalculateOverallLevel( void )
+{
+ if ( !GetResourceCost() )
+ {
+ m_flOverallOwnedPercentage = 0;
+ }
+ else
+ {
+ m_flOverallOwnedPercentage = GetResourceLevel() / GetResourceCost();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CBaseTechnology::GetOverallLevel( void )
+{
+ return m_flOverallOwnedPercentage;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Force this technology to complete itself
+//-----------------------------------------------------------------------------
+void CBaseTechnology::ForceComplete( void )
+{
+ SetResourceLevel( GetResourceCost() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::IsAGoalTechnology( void )
+{
+ return m_bGoalTechnology;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetGoalTechnology( bool bGoal )
+{
+ m_bGoalTechnology = bGoal;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the name of this technology
+// Output : const
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetName( void )
+{
+ return m_pszName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns printable name of this technology
+// Output : const
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetPrintName( void )
+{
+ return m_pszPrintName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetButtonName( void )
+{
+ return m_szButtonName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetTextureName(void )
+{
+ return m_szTextureName;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetTextureId( void )
+{
+ return m_nTextureID;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns description for item
+// Output : const char
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetDescription( int iPlayerClass )
+{
+ // If we have a custom description for the local player's class, return that instead
+ if ( AffectsClass( iPlayerClass ) )
+ {
+ if ( m_ClassResults[iPlayerClass].pszDescription )
+ return m_ClassResults[iPlayerClass].pszDescription;
+ }
+
+ // Otherwise, return the general description
+ return m_pszDescription;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns sound filename to play for this technology
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetSoundFile( int iClass )
+{
+ // Class of 0 is the team sound
+ if ( !iClass )
+ return m_pszTeamSoundFile;
+
+ Assert(iClass > 0 && iClass < TFCLASS_CLASS_COUNT );
+ return m_ClassResults[iClass].pszSoundFile;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns sound to play for this technology
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetSound( int iClass )
+{
+ // Class of 0 is the team sound
+ if ( !iClass )
+ return m_iTeamSound;
+
+ Assert(iClass > 0 && iClass < TFCLASS_CLASS_COUNT );
+ return m_ClassResults[iClass].iSound;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iClass -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::GetAssociateWeaponsForClass( int iClass )
+{
+ if ( !iClass )
+ return false;
+
+ Assert(iClass > 0 && iClass < TFCLASS_CLASS_COUNT );
+ return m_ClassResults[iClass].m_bAssociateWeaponsForClass;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether the technology is available to the team
+// Input : state -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetAvailable( bool state )
+{
+ if ( state != m_bAvailable )
+ {
+ SetDirty( true );
+ }
+
+ m_bAvailable = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Determine whether team posses the technology
+// Output : Returns 1 if available, 0 otherwise
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetAvailable( void )
+{
+ return m_bAvailable;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Zero out team preference counters
+//-----------------------------------------------------------------------------
+void CBaseTechnology::ZeroPreferences( void )
+{
+ if ( m_nPreferenceCount )
+ {
+ SetDirty( true );
+ }
+
+ m_nPreferenceCount = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Increment preference counter
+//-----------------------------------------------------------------------------
+void CBaseTechnology::IncrementPreferences( void )
+{
+ m_nPreferenceCount++;
+ SetDirty( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieve preference count
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetPreferenceCount( void )
+{
+ return m_nPreferenceCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetNumWeaponAssociations( void )
+{
+ return m_nNumWeaponAssociations;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : char const
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetAssociatedWeapon( int index )
+{
+ if ( index < 0 || index >= m_nNumWeaponAssociations )
+ {
+ return "";
+ }
+
+ return m_rgszWeaponAssociation[ index ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *weaponname -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::AddAssociatedWeapon( const char *weaponname )
+{
+ if ( m_nNumWeaponAssociations >= MAX_ASSOCIATED_WEAPONS )
+ {
+ Assert( !"CBaseTechnology::AddAssociatedWeapon: m_nNumWeaponAssociations >= MAX_ASSOCIATED_WEAPONS" );
+ return;
+ }
+ Q_strncpy( m_rgszWeaponAssociation[ m_nNumWeaponAssociations++ ], weaponname, TECHNOLOGY_WEAPONNAME_LENGTH );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetNumberContainedTechs( void )
+{
+ return m_iContainedTechs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetContainedTechName( int iTech )
+{
+ Assert( iTech >= 0 && iTech < m_iContainedTechs );
+ return m_apszContainedTechs[ iTech ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetContainedTech( int iTech, CBaseTechnology *pTech )
+{
+ Assert( iTech >= 0 && iTech < m_iContainedTechs );
+ m_pContainedTechs[iTech] = pTech;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetNumberDependentTechs( void )
+{
+ return m_iDependentTechs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CBaseTechnology::GetDependentTechName( int iTech )
+{
+ Assert( iTech >= 0 && iTech < m_iDependentTechs );
+ return m_apszDependentTechs[ iTech ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetDependentTech( int iTech, CBaseTechnology *pTech )
+{
+ Assert( iTech >= 0 && iTech < m_iDependentTechs );
+ Assert( pTech );
+ m_pDependentTechs[iTech] = pTech;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this tech depends on the specified tech
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::DependsOn( CBaseTechnology *pTech )
+{
+ for ( int i = 0; i < GetNumberDependentTechs(); i++ )
+ {
+ if ( m_pDependentTechs[i] == pTech )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this tech has a dependent tech that's not been completed yet
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::HasInactiveDependencies( void )
+{
+ for ( int i = 0; i < GetNumberDependentTechs(); i++ )
+ {
+ // This condition can occur if there's a bug in the .txt file
+ // where a tech is told to depend on a non-existent tech
+ if (!m_pDependentTechs[i])
+ continue;
+
+ // Client uses m_bActive, Server uses m_bAvailable (?)
+ if ( m_pDependentTechs[i]->GetAvailable() == false && m_pDependentTechs[i]->GetActive() == false )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Dirty bit, used for fast knowledge of when to resend techs
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::IsDirty( void )
+{
+ return m_bDirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetDirty( bool bDirty )
+{
+ m_bDirty = bDirty;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set availability state for item
+// Input : state -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetActive( bool state )
+{
+ m_bActive = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieve availability state
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::GetActive( void )
+{
+ return m_bActive;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set whether this is our local preferred item
+// Input : state -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetPreferred( bool state )
+{
+ m_bPreferred = state;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieve local preferred state
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::GetPreferred( void )
+{
+ return m_bPreferred;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the number of players preferring this tech
+// Input : voters -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetVoters( int voters )
+{
+ m_nVoters = voters;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the number of players preferring this tech
+// Output : int
+//-----------------------------------------------------------------------------
+int CBaseTechnology::GetVoters( void )
+{
+ return m_nVoters;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : hide -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetHidden( bool hide )
+{
+ m_bHidden = hide;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::IsHidden( void )
+{
+ return m_bHidden;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTechnology::ResetHintsGiven( void )
+{
+ m_HintsGiven.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseTechnology::GetHintsGiven( int type )
+{
+ if ( m_HintsGiven.Find( type ) != m_HintsGiven.InvalidIndex() )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : given -
+//-----------------------------------------------------------------------------
+void CBaseTechnology::SetHintsGiven( int type, bool given )
+{
+ if ( given )
+ {
+ if ( m_HintsGiven.Find( type ) != m_HintsGiven.InvalidIndex() )
+ return;
+
+ m_HintsGiven.AddToTail( type );
+ }
+ else
+ {
+ int idx = m_HintsGiven.Find( type );
+ if ( idx == m_HintsGiven.InvalidIndex() )
+ return;
+
+ m_HintsGiven.Remove( idx );
+ }
+}
+
+//====================================================================================================================
+// EVIL GAME DLL ONLY CODE FOR TECHNOLOGIES
+//====================================================================================================================
+#ifndef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTechnology::AddTechnologyToPlayer( CBaseTFPlayer *pPlayer )
+{
+ // Tell playerclasses listed to recalculate their technologies, only if they're listed in the class results
+ if ( pPlayer->PlayerClass() )
+ {
+ if ( m_ClassResults[ pPlayer->PlayerClass() ].bClassTouched )
+ {
+ CPlayerClass *pPlayerClass = pPlayer->GetPlayerClass();
+ pPlayerClass->GainedNewTechnology( this );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTechnology::AddTechnologyToTeam( CTFTeam *pTeam )
+{
+ SetAvailable( true );
+
+ // Enable all the technologies this group contains
+ if ( m_iContainedTechs )
+ {
+ for (int i = 0; i < m_iContainedTechs; i++ )
+ {
+ if ( m_pContainedTechs[i] )
+ {
+ pTeam->EnableTechnology( m_pContainedTechs[i] );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: A technology watcher entity wants to register as a watcher for this technology
+//-----------------------------------------------------------------------------
+void CBaseTechnology::RegisterWatcher( CInfoCustomTechnology *pWatcher )
+{
+ m_aWatchers.AddToTail( pWatcher );
+ pWatcher->UpdateTechPercentage( 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseTechnology::UpdateWatchers( void )
+{
+ // Tell all my watchers
+ for (int i = 0; i < m_aWatchers.Size(); i++ )
+ {
+ m_aWatchers[i]->UpdateTechPercentage( GetOverallLevel() );
+ }
+}
+#else
+//-----------------------------------------------------------------------------
+// Purpose: Client DLL UpdateWatchers does nothing
+//-----------------------------------------------------------------------------
+void CBaseTechnology::UpdateWatchers( void )
+{
+}
+#endif
+
+
+
+
+//====================================================================================================================
+// SHARED TECHNOLOGY TREE
+//====================================================================================================================
+//-----------------------------------------------------------------------------
+// Purpose: Construct raw technology tree
+// Output :
+//-----------------------------------------------------------------------------
+CTechnologyTree::CTechnologyTree( IFileSystem* pFileSystem, int nTeamNumber )
+{
+ // Reset preference counter
+ ClearPreferenceCount();
+
+ // Parse the list from the data file
+ if ( ParseTechnologyFile( m_Technologies, pFileSystem, nTeamNumber, "scripts/technologytree.txt" ) == false )
+ {
+ Assert(0);
+ return;
+ }
+
+ LinkContainedTechnologies();
+ LinkDependentTechnologies();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Link all the contained technologies
+//-----------------------------------------------------------------------------
+void CTechnologyTree::LinkContainedTechnologies( void )
+{
+ for ( int i=0; i < m_Technologies.Size(); i++)
+ {
+ for ( int j = 0; j < m_Technologies[i]->GetNumberContainedTechs(); j++ )
+ {
+ const char *pName = m_Technologies[i]->GetContainedTechName( j );
+ CBaseTechnology *pTech = GetTechnology( pName );
+ if ( pTech )
+ {
+ m_Technologies[i]->SetContainedTech( j, pTech );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Link all the dependent technologies
+//-----------------------------------------------------------------------------
+void CTechnologyTree::LinkDependentTechnologies( void )
+{
+ for ( int i=0; i < m_Technologies.Size(); i++)
+ {
+ for ( int j = 0; j < m_Technologies[i]->GetNumberDependentTechs(); j++ )
+ {
+ const char *pName = m_Technologies[i]->GetDependentTechName( j );
+ CBaseTechnology *pTech = GetTechnology( pName );
+ if ( pTech )
+ {
+ m_Technologies[i]->SetDependentTech( j, pTech );
+ }
+ else
+ {
+ Warning("Unable to find dependent technology %s!\n", pName );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTechnologyTree::~CTechnologyTree( void )
+{
+ Shutdown();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Delete all items in the tree
+//-----------------------------------------------------------------------------
+void CTechnologyTree::Shutdown( void )
+{
+ // Loop through all used items
+ int iSize = m_Technologies.Size();
+ for ( int i = iSize-1; i >= 0; i-- )
+ {
+ CBaseTechnology *pItem = m_Technologies[ i ];
+ delete pItem;
+ }
+ m_Technologies.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a new technology to the tree
+//-----------------------------------------------------------------------------
+void CTechnologyTree::AddTechnologyFile( IFileSystem* pFileSystem, int nTeamNumber, char *sFileName )
+{
+ ParseTechnologyFile( m_Technologies, pFileSystem, nTeamNumber, sFileName );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the index of the specified item
+//-----------------------------------------------------------------------------
+int CTechnologyTree::GetIndex( CBaseTechnology *pItem )
+{
+ for ( int i=0; i < m_Technologies.Size(); i++)
+ {
+ if ( m_Technologies[i] == pItem )
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Retrieve item by index
+//-----------------------------------------------------------------------------
+CBaseTechnology *CTechnologyTree::GetTechnology( int index )
+{
+ if ( index < 0 || index >= m_Technologies.Size() )
+ return NULL;
+
+ return m_Technologies[ index ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseTechnology* CTechnologyTree::GetTechnology( const char *pName )
+{
+ for ( int i=0; i < m_Technologies.Size(); i++)
+ {
+ if( stricmp( pName, m_Technologies[i]->GetName() ) == 0 )
+ return m_Technologies[i];
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return current number of objects
+// Output : int
+//-----------------------------------------------------------------------------
+int CTechnologyTree::GetNumberTechnologies( void )
+{
+ return m_Technologies.Size();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set preferred item ( turns off all other preferences first )
+//-----------------------------------------------------------------------------
+void CTechnologyTree::SetPreferredTechnology( CBaseTechnology *pItem )
+{
+ // Turn all of the others off
+ for ( int i = 0; i < m_Technologies.Size(); i++ )
+ {
+ CBaseTechnology *item = m_Technologies[ i ];
+ item->SetPreferred( false );
+ }
+
+ // Turn this one on
+ if ( pItem )
+ {
+ pItem->SetPreferred( true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the technology preferred by this client
+//-----------------------------------------------------------------------------
+CBaseTechnology* CTechnologyTree::GetPreferredTechnology( void )
+{
+ // Turn all of the others off
+ for ( int i = 0; i < m_Technologies.Size(); i++ )
+ {
+ CBaseTechnology *item = m_Technologies[ i ];
+ if ( item->GetPreferred() )
+ return item;
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the number of players who've voted on techs
+//-----------------------------------------------------------------------------
+int CTechnologyTree::GetPreferenceCount( void )
+{
+ return m_nPreferenceCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Zero global preference counters
+//-----------------------------------------------------------------------------
+void CTechnologyTree::ClearPreferenceCount( void )
+{
+ m_nPreferenceCount = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Increment preference counter
+//-----------------------------------------------------------------------------
+void CTechnologyTree::IncrementPreferences( void )
+{
+ m_nPreferenceCount++;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the most voted-for technologies
+// Input : iDesireLevel: 1 = Most voted for, 2 = 2nd most voted for, etc
+//-----------------------------------------------------------------------------
+CBaseTechnology *CTechnologyTree::GetDesiredTechnology( int iDesireLevel )
+{
+ Assert( iDesireLevel > 0 && iDesireLevel < m_Technologies.Size() );
+
+ // Dump the techs into a temporary array
+ CBaseTechnology *pSortedTechs[ MAX_TECHNOLOGIES ];
+ int iMaxTech = 0;
+ for ( int i = 0; i < m_Technologies.Size(); i++ )
+ {
+ // Skip Techs that are already available
+ CBaseTechnology *technology = m_Technologies[i];
+ if(!technology)
+ continue;
+
+ if ( technology->GetAvailable() == false )
+ {
+ pSortedTechs[iMaxTech] = m_Technologies[i];
+ iMaxTech++;
+ }
+ }
+
+ // Not enough unresearched techs?
+ if ( iMaxTech < iDesireLevel )
+ return NULL;
+
+ // Bubble sort the tech array into order of desire
+ int swapped = 1;
+ while ( swapped )
+ {
+ swapped = 0;
+ for ( int i = 1; i < iMaxTech; i++ )
+ {
+ if ( pSortedTechs[i]->GetPreferenceCount() > pSortedTechs[i-1]->GetPreferenceCount() )
+ {
+ CBaseTechnology *pTemp = pSortedTechs[i];
+ pSortedTechs[i] = pSortedTechs[i-1];
+ pSortedTechs[i-1] = pTemp;
+ swapped = 1;
+ }
+ }
+ }
+
+ return pSortedTechs[ iDesireLevel - 1 ];
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find out what percentage of techs in a given level this team owns
+//-----------------------------------------------------------------------------
+float CTechnologyTree::GetPercentageOfTechLevelOwned( int iTechLevel )
+{
+ float fTotalTechs = 0;
+ float fTechsOwned = 0;
+
+ for ( int i = 0; i < m_Technologies.Size(); i++ )
+ {
+ CBaseTechnology *technology = m_Technologies[i];
+ if ( !technology )
+ continue;
+
+ if ( technology->GetLevel() != iTechLevel )
+ continue;
+
+ fTotalTechs++;
+
+ // Do we have it?
+ if ( technology->GetAvailable() )
+ {
+ fTechsOwned++;
+ }
+ }
+
+ if ( !fTotalTechs )
+ return 0.0;
+
+ return ( fTechsOwned / fTotalTechs );
+}
diff --git a/game/shared/tf2/techtree.h b/game/shared/tf2/techtree.h
new file mode 100644
index 0000000..bac9764
--- /dev/null
+++ b/game/shared/tf2/techtree.h
@@ -0,0 +1,320 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#if !defined( TECHTREE_H )
+#define TECHTREE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// Evil, Game DLL only code
+#ifndef CLIENT_DLL
+class CBaseTFPlayer;
+class CTFTeam;
+class CInfoCustomTechnology;
+#endif
+
+class IFileSystem;
+
+//===========================================================================================
+// Technology tree defines
+#define MAX_TF_TECHLEVELS 6 // Number of TF2 tech levels
+#define TECHLEVEL_PERCENTAGE_NEEDED 0.5 // Percentage of a tech level that must be owned before the next tech level becomes available
+#define MAX_TECHNOLOGIES 128 // Max number of resource types
+#define MAX_ASSOCIATED_WEAPONS 2 // Max number of weapons that a tech can be associated with
+
+// Indexes into resource arrays
+#define NORMAL_RESOURCES 0
+#define PROCESSED_RESOURCES 1
+#define RESOURCE_TYPES 2
+
+// Tech
+#define TECHNOLOGY_NAME_LENGTH 64 // Max length of a tech name
+#define TECHNOLOGY_PRINTNAME_LENGTH 128 // Max length of a tech's print name
+#define TECHNOLOGY_DESC_LENGTH 256 // Max length of a tech's description
+#define MAX_CONTAINED_TECHNOLOGIES 16 // Max number of technologies that can be contained within another technology
+#define MAX_DEPENDANT_TECHNOLOGIES 16 // Max number of technologies that a tech can depend on
+#define TECHNOLOGY_SOUNDFILENAME_LENGTH 256
+#define TECHNOLOGY_TEXTURENAME_LENGTH 128
+#define TECHNOLOGY_BUTTONNAME_LENGTH 64
+#define TECHNOLOGY_WEAPONNAME_LENGTH 128
+
+// Color codes for Resources
+struct rescolor
+{
+ int r;
+ int g;
+ int b;
+};
+
+// Class result structure for technologies
+struct classresult_t
+{
+ bool bClassTouched; // This technology directly affects this class
+ char pszSoundFile[TECHNOLOGY_SOUNDFILENAME_LENGTH]; // Filename of the sound
+ int iSound; // Sound played to members of this class when this technology is achieved
+ char pszDescription[TECHNOLOGY_DESC_LENGTH]; // Description for this technology shown only to this class
+
+ // If true, then we should determine what weapons should be given to the player
+ // of this class when this technology is received by looking at the "associated_weapons"
+ // data
+ bool m_bAssociateWeaponsForClass;
+};
+
+extern char sResourceName[32];
+extern rescolor sResourceColor;
+
+
+#include "utlvector.h"
+#include "tf_shareddefs.h"
+
+
+//===========================================================================================
+//-----------------------------------------------------------------------------
+// Purpose: A Technology
+//-----------------------------------------------------------------------------
+class CBaseTechnology
+{
+public:
+ // Constructions
+ CBaseTechnology( void );
+ virtual ~CBaseTechnology( void );
+
+ // Data read from the data file
+ virtual void SetName( const char *pName );
+ virtual void SetPrintName( const char *pName );
+ virtual void SetDescription( const char *pDesc );
+ virtual void SetButtonName( const char *pName );
+ virtual void SetLevel( int iLevel );
+ virtual void SetCost( float fResourceCost );
+ virtual void SetClassResultSound( int iClass, const char *pSound );
+ virtual void SetClassResultSound( int iClass, int iSound );
+ virtual void SetClassResultDescription( int iClass, const char *pDesc );
+ virtual void SetClassResultAssociateWeapons( int iClass, bool associate );
+ virtual void AddContainedTechnology( const char *pszTech );
+ virtual void AddDependentTechnology( const char *pszTech );
+
+ // Returns true if the specified class is affected by this technology, or any contained techs
+ virtual bool AffectsClass( int iClass );
+
+ virtual bool IsClassUpgrade( void );
+ virtual bool IsVehicle( void );
+ virtual bool IsTechLevelUpgrade( void );
+ virtual bool IsResourceTech( void );
+
+ virtual void SetHidden( bool hide );
+ virtual bool IsHidden( void );
+
+ // Used by client to avoid giving you the same hint twice for a technology
+ // during a game/session
+ virtual void ResetHintsGiven( void );
+ virtual bool GetHintsGiven( int type );
+ virtual void SetHintsGiven( int type, bool given );
+
+ // Returns the level to which this technology belongs
+ virtual int GetLevel( void );
+ // Returns the internal name of the technology ( no spaces )
+ virtual const char *GetName( void );
+ // Returns the printable name of the technology
+ virtual const char *GetPrintName( void );
+ // Returns the button name of the technology;
+ virtual const char *GetButtonName( void );
+ // Returns the non-class specific description of this technology
+ virtual const char *GetDescription( int iPlayerClass );
+ // Returns the sound to play for this technology
+ virtual const char *GetSoundFile( int iClass );
+ virtual int GetSound( int iClass );
+ // Set availability of the technology for the specified team
+ virtual void SetAvailable( bool state );
+ // Returns true if the team has the technology
+ virtual int GetAvailable( void );
+ // Zero out all preference/voting by players
+ virtual void ZeroPreferences( void );
+ // Add one to the preference count for this technology for the specified team
+ virtual void IncrementPreferences( void );
+ // Retrieve the number of player's who want to vote for this technology
+ virtual int GetPreferenceCount( void );
+
+ // Retrieves the cost of purchasing the technology (doesn't factor in the resource levels)
+ float GetResourceCost( void );
+
+ // Retrieves the current amount of resources spent on the technology
+ float GetResourceLevel( void );
+
+ // Sets a resource level to an amount
+ void SetResourceLevel( float flResourceLevel );
+ // Spends resources on buying this technology
+ bool IncreaseResourceLevel( float flResourcesToSpend );
+ // Figure out my overall owned percentage
+ void RecalculateOverallLevel( void );
+ float GetOverallLevel( void );
+ void ForceComplete( void );
+
+ // Goal technologies ( Techs related to a team's goal in a map )
+ bool IsAGoalTechnology( void );
+ void SetGoalTechnology( bool bGoal );
+
+ // Check if class wants to enumerate weapon associations
+ bool GetAssociateWeaponsForClass( int iClass );
+
+ // Weapon associatations
+ int GetNumWeaponAssociations( void );
+ char const *GetAssociatedWeapon( int index );
+ void AddAssociatedWeapon( const char *weaponname );
+
+ // Contained Technology access
+ int GetNumberContainedTechs( void );
+ const char *GetContainedTechName( int iTech );
+ void SetContainedTech( int iTech, CBaseTechnology *pTech );
+
+ // Dependent Technology access
+ int GetNumberDependentTechs( void );
+ const char *GetDependentTechName( int iTech );
+ void SetDependentTech( int iTech, CBaseTechnology *pTech );
+ bool DependsOn( CBaseTechnology *pTech );
+ bool HasInactiveDependencies( void );
+
+ // Dirty bit, used for fast knowledge of when to resend techs
+ bool IsDirty( void );
+ void SetDirty( bool bDirty );
+
+// Evil, Game DLL only code
+#ifndef CLIENT_DLL
+ // The technology has been acquired by the team.
+ virtual void AddTechnologyToTeam( CTFTeam *pTeam );
+ // The technology has just been acquired, for each player on the acquiring team
+ // ask the technology to add any necessary weapons/items/abilities/modifiers, etc.
+ virtual void AddTechnologyToPlayer( CBaseTFPlayer *player );
+ // A technology watcher entity wants to register as a watcher for this technology
+ virtual void RegisterWatcher( CInfoCustomTechnology *pWatcher );
+ CUtlVector< CInfoCustomTechnology* > m_aWatchers;
+#endif
+
+ void UpdateWatchers( void );
+
+ // Hud Data
+ void SetActive( bool state );
+ bool GetActive( void );
+ void SetPreferred( bool state );
+ bool GetPreferred( void );
+ void SetVoters( int voters );
+ int GetVoters( void );
+
+ void SetTextureName( const char *texture );
+ const char *GetTextureName( void );
+ void SetTextureId( int id );
+ int GetTextureId( void );
+
+private:
+ // Name of the technology. Used to identify it in code.
+ char m_pszName[ TECHNOLOGY_NAME_LENGTH ];
+ // Print name of the technology. Used to print the name of this technology to users.
+ char m_pszPrintName[ TECHNOLOGY_PRINTNAME_LENGTH ];
+ // Button name of technology in the tech tree
+ char m_szButtonName[ TECHNOLOGY_BUTTONNAME_LENGTH ];
+ // Description of the technology
+ char m_pszDescription[ TECHNOLOGY_DESC_LENGTH ];
+ // Level to which the technology belongs
+ int m_nTechLevel;
+ // Sound played to the entire team when this technology is received
+ char m_pszTeamSoundFile[ TECHNOLOGY_SOUNDFILENAME_LENGTH ];
+ int m_iTeamSound;
+ // Results for this technology when it's achieved, on a per-class basis
+ classresult_t m_ClassResults[ TFCLASS_CLASS_COUNT ];
+
+ // Resource costs
+ float m_fResourceCost;
+ // Resource levels (amount of resource spent on the technology so far)
+ float m_fResourceLevel;
+ float m_flOverallOwnedPercentage;
+
+ // Technologies contained within this one
+ char m_apszContainedTechs[ MAX_CONTAINED_TECHNOLOGIES ][ TECHNOLOGY_NAME_LENGTH ];
+ int m_iContainedTechs;
+ CBaseTechnology *m_pContainedTechs[ MAX_CONTAINED_TECHNOLOGIES ];
+
+ // Technologies this tech depends on
+ char m_apszDependentTechs[ MAX_DEPENDANT_TECHNOLOGIES ][ TECHNOLOGY_NAME_LENGTH ];
+ int m_iDependentTechs;
+ CBaseTechnology *m_pDependentTechs[ MAX_DEPENDANT_TECHNOLOGIES ];
+
+ // Weapon association
+ int m_nNumWeaponAssociations;
+ char m_rgszWeaponAssociation[ MAX_ASSOCIATED_WEAPONS ][ TECHNOLOGY_WEAPONNAME_LENGTH ];
+
+ // Does the team have access to the technology
+ bool m_bAvailable;
+
+ // Is this a "placeholder" tech that shouldn't show up in the real tree
+ bool m_bHidden;
+
+ CUtlVector< int > m_HintsGiven;
+
+ // Count of how many team members voted for this technology for spending resources
+ int m_nPreferenceCount;
+
+ bool m_bGoalTechnology; // True if this tech's related to a team's goal in the current map
+ bool m_bClassUpgrade; // True if the tech unlocks a new class
+ bool m_bVehicle; // True if the tech unlocks a vehicle
+ bool m_bTechLevelUpgrade; // True if the tech unlocks a new tech level
+ bool m_bResourceTech; // True if related to resource gathering
+
+ // Dirty bit, used for fast knowledge of when to resend techs
+ bool m_bDirty;
+
+ // Hud data
+ bool m_bActive;
+ bool m_bPreferred;
+ int m_nVoters;
+
+ int m_nTextureID;
+ char m_szTextureName[ TECHNOLOGY_TEXTURENAME_LENGTH ];
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: The Technology Tree.
+//-----------------------------------------------------------------------------
+class CTechnologyTree
+{
+public:
+ // Construction
+ CTechnologyTree( IFileSystem* pFileSystem, int nTeamNumber );
+ virtual ~CTechnologyTree( void );
+
+ // Startup/shutdown
+ void Shutdown( void );
+
+ // Accessors
+ void AddTechnologyFile( IFileSystem* pFileSystem, int nTeamNumber, char *sFileName );
+ void LinkContainedTechnologies( void );
+ void LinkDependentTechnologies( void );
+ int GetIndex( CBaseTechnology *pItem ); // Get the index of the specified item
+ CBaseTechnology *GetTechnology( int index );
+ CBaseTechnology *GetTechnology( const char *pName );
+ float GetPercentageOfTechLevelOwned( int iTechLevel );
+
+ // Size of list
+ int GetNumberTechnologies( void );
+
+ // Local client's preferred item
+ void SetPreferredTechnology( CBaseTechnology *pItem );
+ CBaseTechnology *GetPreferredTechnology( void );
+
+ // Preference handling
+ void ClearPreferenceCount( void );
+ void IncrementPreferences( void );
+ int GetPreferenceCount( void ); // Get the number of players who've voted on techs
+ CBaseTechnology *GetDesiredTechnology( int iDesireLevel );
+
+ // Growable list of technologies
+ CUtlVector< CBaseTechnology * > m_Technologies;
+
+ int m_nPreferenceCount;
+};
+
+
+#endif // TECHTREE_H \ No newline at end of file
diff --git a/game/shared/tf2/techtree_parse.cpp b/game/shared/tf2/techtree_parse.cpp
new file mode 100644
index 0000000..4b05d2b
--- /dev/null
+++ b/game/shared/tf2/techtree_parse.cpp
@@ -0,0 +1,183 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Shared code that parse the Technology Tree on the client & server.
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "utlvector.h"
+#include "tf_shareddefs.h"
+#include "techtree.h"
+#include <KeyValues.h>
+#include "filesystem.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse a class result chunk inside a technology keyvalue
+//-----------------------------------------------------------------------------
+void ParseClassResult( CBaseTechnology *pTechnology, KeyValues *pkvResult, int iClass )
+{
+ if ( !pkvResult )
+ return;
+
+ // All is a special case, used by general technologies
+ if ( iClass == TFCLASS_CLASS_COUNT )
+ {
+ for (int i = TFCLASS_RECON; i < TFCLASS_CLASS_COUNT; i++)
+ {
+ pTechnology->SetClassResultSound( i, pkvResult->GetString( "sound", NULL ) );
+ pTechnology->SetClassResultDescription( i, pkvResult->GetString( "description", NULL ) );
+ pTechnology->SetClassResultAssociateWeapons( i, pkvResult->GetInt( "associateweapons", 0 ) ? true : false );
+ }
+ }
+ else
+ {
+ pTechnology->SetClassResultSound( iClass, pkvResult->GetString( "sound", NULL ) );
+ pTechnology->SetClassResultDescription( iClass, pkvResult->GetString( "description", NULL ) );
+ pTechnology->SetClassResultAssociateWeapons( iClass, pkvResult->GetInt( "associateweapons", 0 ) ? true : false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse a technology from a keyvalues chunk in the data file
+//-----------------------------------------------------------------------------
+void ParseTechnology( CBaseTechnology *pTechnology, KeyValues *pkvTech )
+{
+ // Get the general data
+ pTechnology->SetName( pkvTech->GetName() );
+ pTechnology->SetPrintName( pkvTech->GetString( "printname", "" ) );
+ // Use the print name if no button name specified
+ pTechnology->SetButtonName( pkvTech->GetString( "buttonname", pTechnology->GetPrintName() ) );
+ pTechnology->SetDescription( pkvTech->GetString( "description", "" ) );
+ pTechnology->SetTextureName( pkvTech->GetString( "texture", "" ) );
+ pTechnology->SetLevel( pkvTech->GetInt( "level", 0 ) );
+ pTechnology->SetGoalTechnology( pkvTech->GetInt( "goal", 0 ) > 0 );
+ pTechnology->SetHidden( pkvTech->GetInt( "hidden", 0 ) != 0 );
+
+ // Retrieve weapon associations
+ KeyValues *pkvWeaponAssociations = pkvTech->FindKey( "associated_weapons" );
+ if ( pkvWeaponAssociations )
+ {
+ KeyValues *pkvWA = pkvWeaponAssociations->GetFirstSubKey();
+ while ( pkvWA )
+ {
+ const char *weaponname = pkvWA->GetString();
+ if ( weaponname && weaponname[ 0 ] )
+ {
+ pTechnology->AddAssociatedWeapon( weaponname );
+ }
+
+ pkvWA = pkvWA->GetNextKey();
+ }
+ }
+
+ // Get the cost
+ pTechnology->SetCost( pkvTech->GetFloat( "resourcecost", 0.0 ) );
+
+ // Get the class results
+ KeyValues *pkvClassResults = pkvTech->FindKey( "class_results" );
+ if ( pkvClassResults )
+ {
+ // Try and get each class result
+ for ( int iClass=0; iClass < TFCLASS_CLASS_COUNT; iClass++ )
+ {
+ ParseClassResult( pTechnology, pkvClassResults->FindKey( GetTFClassInfo( iClass )->m_pClassName ), iClass );
+ }
+
+ ParseClassResult( pTechnology, pkvClassResults->FindKey( "all" ), TFCLASS_CLASS_COUNT );
+ }
+
+ // Get any technologies contained within this one
+ KeyValues *pkvTechnologies = pkvTech->FindKey( "technologies" );
+ if ( pkvTechnologies )
+ {
+ KeyValues *pkvTechnology = pkvTechnologies->GetFirstSubKey();
+ int iContainedTechs = 0;
+ while ( pkvTechnology )
+ {
+ if ( iContainedTechs >= MAX_CONTAINED_TECHNOLOGIES )
+ break;
+
+ pTechnology->AddContainedTechnology( pkvTechnology->GetString() );
+
+ iContainedTechs++;
+ pkvTechnology = pkvTechnology->GetNextKey();
+ }
+ }
+
+ // Get any dependencies for this tech
+ KeyValues *pkvDependencies = pkvTech->FindKey( "dependencies" );
+ if ( pkvDependencies )
+ {
+ KeyValues *pkvTechnology = pkvDependencies->GetFirstSubKey();
+ int iDependentTechs = 0;
+ while ( pkvTechnology )
+ {
+ if ( iDependentTechs >= MAX_DEPENDANT_TECHNOLOGIES )
+ break;
+
+ pTechnology->AddDependentTechnology( pkvTechnology->GetString() );
+
+ iDependentTechs++;
+ pkvTechnology = pkvTechnology->GetNextKey();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Parse the technology tree file and dump the data into the utlvector list
+//-----------------------------------------------------------------------------
+bool ParseTechnologyFile( CUtlVector< CBaseTechnology * > &pTechnologyList, IFileSystem* filesystem, int nTeamNumber, char *sFileName )
+{
+ // Open the technology tree datafile
+ KeyValues *pkvTechTreeFile = new KeyValues( "TechTreeDataFile" );
+ if ( pkvTechTreeFile->LoadFromFile( filesystem, sFileName, "GAME" ) == false )
+ return false;
+
+ // Parse the list of techs
+ KeyValues *pkvTech = pkvTechTreeFile->GetFirstSubKey();
+ while ( pkvTech )
+ {
+ // Reached the maximum number of techs allowed?
+ if ( pTechnologyList.Size() >= MAX_TECHNOLOGIES )
+ {
+ pkvTechTreeFile->deleteThis();
+ return false;
+ }
+
+ // Create the technology
+ CBaseTechnology *pTechnology = NULL;
+ int nTeamTech = pkvTech->GetInt( "team", 0 );
+ if ((nTeamTech == 0) || (nTeamTech == nTeamNumber))
+ {
+ pTechnology = new CBaseTechnology();
+ ParseTechnology( pTechnology, pkvTech );
+
+ // Find out if it's already in the list
+ for ( int i = 0; i < pTechnologyList.Size(); i++ )
+ {
+ if ( !strcmp(pTechnologyList[i]->GetName(), pTechnology->GetName() ) )
+ {
+ // Found it in the tree already, so delete and continue
+ delete pTechnology;
+ pTechnology = NULL;
+ break;
+ }
+ }
+ }
+
+ // If we haven't deleted it, add it to the list
+ if ( pTechnology )
+ {
+ pTechnologyList.AddToTail( pTechnology );
+ }
+
+ pkvTech = pkvTech->GetNextKey();
+ }
+
+ pkvTechTreeFile->deleteThis();
+ return true;
+}
+
diff --git a/game/shared/tf2/tf_gamemovement.cpp b/game/shared/tf2/tf_gamemovement.cpp
new file mode 100644
index 0000000..46366d9
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement.cpp
@@ -0,0 +1,2149 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement.h"
+#include "in_buttons.h"
+#include "tier0/vprof.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+
+#define SPEED_STOP_THRESHOLD 1.0f
+#define BUMP_MAX_COUNT 8
+
+//#define IMPACT_NORMAL_FLOOR 0.35f
+#define IMPACT_NORMAL_FLOOR 0.7f
+#define IMPACT_NORMAL_WALL 0.0f
+
+enum
+{
+ MOVEMENT_BLOCKED_NONE = 0x0,
+ MOVEMENT_BLOCKED_WALL = 0x1,
+ MOVEMENT_BLOCKED_FLOOR = 0x2,
+ MOVEMENT_BLOCKED_ALL = 0x4
+};
+
+#define SPEED_CROP_FRACTION_WALKING 0.4f
+#define SPEED_CROP_FRACTION_USING 0.3f
+#define SPEED_CROP_FRACTION_DUCKING 0.3f
+
+char *va(char *format, ...);
+
+extern bool g_bMovementOptimizations;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::CategorizePosition( void )
+{
+ VPROF( "CTFGameMovement::CategorizePosition" );
+
+ // Doing this before we move may introduce a potential latency in water detection, but
+ // doing it after can get us stuck on the bottom in water if the amount we move up
+ // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call
+ // this several times per frame, so we really need to avoid sticking to the bottom of
+ // water on each call, and the converse case will correct itself if called twice.
+ CheckWater();
+
+ trace_t trace;
+ Vector vStart( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z );
+ Vector vEnd( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - 66 );
+
+ // Assume we are not on the ground
+ SetGroundEntity( NULL );
+ m_pSurfaceData = NULL;
+ m_surfaceFriction = 1.0f;
+ m_chTextureType = 'C';
+
+ // Check our velocity in z (are we shooting up really fast - then we are not on ground).
+ if ( mv->m_vecVelocity.z <= 180.0f )
+ {
+ // Move downward.
+ TracePlayerBBox( vStart, vEnd, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+
+
+ // Check to see if we are on the ground
+ if ( ( ( trace.plane.normal.z >= IMPACT_NORMAL_FLOOR ) || trace.IsDispSurfaceWalkable() ) && ( trace.fraction < 0.06f ) )
+ {
+ SetGroundEntity( &trace );
+ VectorCopy( trace.plane.normal, m_vecGroundNormal );
+
+ // Add standing on object to touch list
+ if ( trace.DidHitNonWorldEntity() )
+ {
+ MoveHelper()->AddToTouched( trace, mv->m_vecVelocity );
+ }
+
+ // Reset water jumping.
+ player->m_flWaterJumpTime = 0.0f;
+
+ // If we could make the move, drop us down that 1 pixel
+ if ( ( int )player->GetWaterLevel() < WL_Waist && !trace.startsolid && !trace.allsolid )
+ {
+ // check distance we would like to move -- this is supposed to just keep up
+ // "on the ground" surface not stap us back to earth
+ mv->m_vecAbsOrigin = vStart + trace.fraction * ( vEnd - vStart );
+// VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
+ }
+ }
+
+ // NOTE: should this be surrounded by trace.fraction <= 1.0f ?????
+
+ // Setup surface properties.
+ {
+ VPROF( "CTFGameMovement::CategorizePosition / Surface Props" );
+ IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
+ m_surfaceProps = trace.surface.surfaceProps;
+ m_pSurfaceData = physprops->GetSurfaceData( m_surfaceProps );
+ physprops->GetPhysicsProperties( m_surfaceProps, NULL, NULL, &m_surfaceFriction, NULL );
+ }
+
+ // HACKHACK: Scale this to fudge the relationship between vphysics friction values and player friction values.
+ // A value of 0.8f feels pretty normal for vphysics, whereas 1.0f is normal for players.
+ // This scaling trivially makes them equivalent. REVISIT if this affects low friction surfaces too much.
+ m_surfaceFriction *= 1.25f;
+ if ( m_surfaceFriction > 1.0f )
+ m_surfaceFriction = 1.0f;
+ m_chTextureType = m_pSurfaceData->game.material;
+ }
+
+ // If we are not on the ground...
+ if ( player->GetGroundEntity() == NULL )
+ {
+ player->m_Local.m_flFallVelocity = -mv->m_vecVelocity.z;
+ }
+
+ // Store off the starting water level
+ m_nOldWaterLevel = player->GetWaterLevel();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::HandleLadder( void )
+{
+ // No ladder movement if the player is dead or on a train.
+ if ( player->pl.deadflag || ( player->GetFlags() & FL_ONTRAIN ) )
+ return;
+
+ // Ladder initialization.
+ m_nOnLadder = 0;
+
+ if ( !BaseClass::LadderMove() && ( player->GetMoveType() == MOVETYPE_LADDER ) )
+ {
+ // clear ladder stuff unless player is noclipping or being lifted by barnacle.
+ // it will be set immediately again next frame if necessary
+ player->SetMoveType( MOVETYPE_WALK );
+ player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::SpeedCrop( void )
+{
+ // Verify speed hasn't been cropped already (shouldn't have!!!).
+ if ( m_iSpeedCropped & SPEED_CROPPED_DUCK )
+ return;
+
+ // Set the speed cropped flag.
+ m_iSpeedCropped |= SPEED_CROPPED_DUCK;
+
+ // If the "walking" key is pressed -- crop speed.
+ if ( mv->m_nButtons & IN_SPEED )
+ {
+ mv->m_flForwardMove *= SPEED_CROP_FRACTION_WALKING;
+ mv->m_flSideMove *= SPEED_CROP_FRACTION_WALKING;
+ mv->m_flUpMove *= SPEED_CROP_FRACTION_WALKING;
+ }
+ // If the "use" key is pressed and the player is on the ground -- crop speed.
+ else if ( ( mv->m_nButtons & IN_USE ) && ( player->GetGroundEntity() != NULL ) )
+ {
+ mv->m_flForwardMove *= SPEED_CROP_FRACTION_USING;
+ mv->m_flSideMove *= SPEED_CROP_FRACTION_USING;
+ mv->m_flUpMove *= SPEED_CROP_FRACTION_USING;
+ }
+ // If the player is "ducking" -- crop speed
+ else if ( player->GetFlags() & FL_DUCKING )
+ {
+ mv->m_flForwardMove *= SPEED_CROP_FRACTION_DUCKING;
+ mv->m_flSideMove *= SPEED_CROP_FRACTION_DUCKING;
+ mv->m_flUpMove *= SPEED_CROP_FRACTION_DUCKING;
+ }
+
+ // Moving backwards happens more slowly
+ float flAngle = atan2( mv->m_flSideMove, mv->m_flForwardMove ) / M_PI;
+ flAngle = 2.0f * (fabs(flAngle) - 0.5f);
+ flAngle = clamp( flAngle, 0.0f, 1.0f );
+ float flFactor = 1.0f - fabs(flAngle) * (1.0f - sv_backspeed.GetFloat());
+
+ mv->m_flForwardMove *= flFactor;
+ mv->m_flSideMove *= flFactor;
+}
+
+
+//-----------------------------------------------------------------------------
+// Figures out how the constraint should slow us down
+//-----------------------------------------------------------------------------
+float CTFGameMovement::ComputeConstraintSpeedFactor( void )
+{
+ // If we have a constraint, slow down because of that too...
+ // Get the TF movement data.
+ CTFMoveData *pTFMove = TFMove();
+ if ( !pTFMove || pTFMove->m_flConstraintRadius == 0.0f )
+ return 1.0f;
+
+ float flDistSq = mv->m_vecAbsOrigin.DistToSqr( pTFMove->m_vecConstraintCenter );
+
+ float flOuterRadiusSq = pTFMove->m_flConstraintRadius * pTFMove->m_flConstraintRadius;
+ float flInnerRadiusSq = pTFMove->m_flConstraintRadius - pTFMove->m_flConstraintWidth;
+ flInnerRadiusSq *= flInnerRadiusSq;
+
+ // Only slow us down if we're inside the constraint ring
+ if ((flDistSq <= flInnerRadiusSq) || (flDistSq >= flOuterRadiusSq))
+ return 1.0f;
+
+ // Only slow us down if we're running away from the center
+ Vector vecDesired;
+ VectorMultiply( m_vecForward, mv->m_flForwardMove, vecDesired );
+ VectorMA( vecDesired, mv->m_flSideMove, m_vecRight, vecDesired );
+ VectorMA( vecDesired, mv->m_flUpMove, m_vecUp, vecDesired );
+
+ Vector vecDelta;
+ VectorSubtract( mv->m_vecAbsOrigin, pTFMove->m_vecConstraintCenter, vecDelta );
+ VectorNormalize( vecDelta );
+ VectorNormalize( vecDesired );
+ if (DotProduct( vecDelta, vecDesired ) < 0.0f)
+ return 1.0f;
+
+ float flFrac = (sqrt(flDistSq) - (pTFMove->m_flConstraintRadius - pTFMove->m_flConstraintWidth)) / pTFMove->m_flConstraintWidth;
+
+ float flSpeedFactor = Lerp( flFrac, 1.0f, pTFMove->m_flConstraintSpeedFactor );
+ return flSpeedFactor;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called in PrePlayerMove(), this function clamps the player's overall
+// speed. It is clamped to either the maximum client's or server's
+// speed (whichever is lower).
+//-----------------------------------------------------------------------------
+void CTFGameMovement::SetupSpeed( void )
+{
+ // Reset the outgoing applied velocity.
+ mv->m_outWishVel.Init();
+
+ // Don't clamp speed if we are sin an "ISOMETRIC" or "NOCLIP" movetype.
+ if ( ( player->GetMoveType() == MOVETYPE_ISOMETRIC ) ||
+ ( player->GetMoveType() == MOVETYPE_NOCLIP ) )
+ return;
+
+ // Some events negate speed, check for these first.
+ if ( ( player->GetFlags() & FL_FROZEN ) || ( player->GetFlags() & FL_ONTRAIN ) || IsDead() )
+ {
+ mv->m_flForwardMove = 0;
+ mv->m_flSideMove = 0;
+ mv->m_flUpMove = 0;
+ return;
+ }
+
+ // Calculate the players max speed given movements.
+ float flSpeed = ( mv->m_flForwardMove * mv->m_flForwardMove ) +
+ ( mv->m_flSideMove * mv->m_flSideMove ) +
+ ( mv->m_flUpMove * mv->m_flUpMove );
+ if ( flSpeed == 0.0f )
+ return;
+
+ flSpeed = sqrt( flSpeed );
+
+ // NOTE: The client max speed was set to the movement max speed (mv->m_flMaxSpeed)
+ // in the SetupMove, however the _ProcessMovement code post sets
+ // mv->m_flMaxSpeed to the maximum server speed (sv_maxspeed),
+ // thus, the need to the check. If we forego the maximum server speed,
+ // this code can be removed.
+ if ( mv->m_flClientMaxSpeed )
+ {
+ mv->m_flMaxSpeed = MIN( mv->m_flClientMaxSpeed, mv->m_flMaxSpeed );
+ }
+
+ // Slow down by the speed factor
+ float flSpeedFactor = 1.0f;
+ if (m_pSurfaceData)
+ {
+ flSpeedFactor = m_pSurfaceData->game.maxSpeedFactor;
+ }
+
+ // If we have a constraint, slow down because of that too...
+ // Get the TF movement data
+ float flConstraintSpeedFactor = ComputeConstraintSpeedFactor();
+ if (flConstraintSpeedFactor < flSpeedFactor)
+ flSpeedFactor = flConstraintSpeedFactor;
+
+ mv->m_flMaxSpeed *= flSpeedFactor;
+
+ if ( flSpeed > mv->m_flMaxSpeed )
+ {
+ float flRatio = mv->m_flMaxSpeed / flSpeed;
+ mv->m_flForwardMove *= flRatio;
+ mv->m_flSideMove *= flRatio;
+ mv->m_flUpMove *= flRatio;
+ }
+
+ // Crop speed if necessary.
+ SpeedCrop();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::FinishUnDuck( void )
+{
+ int i;
+ trace_t trace;
+ Vector newOrigin;
+
+ Vector vDuckHullMin = GetPlayerMins( true );
+ Vector vStandHullMin = GetPlayerMins( false );
+
+ VectorCopy( mv->m_vecAbsOrigin, newOrigin );
+
+ if ( player->GetGroundEntity() != NULL )
+ {
+ for ( i = 0; i < 3; i++ )
+ {
+ newOrigin[i] += ( vDuckHullMin[i] - vStandHullMin[i] );
+ }
+ }
+ else
+ {
+ Vector viewDelta = player->GetViewOffset() - GetPlayerViewOffset( false );
+ VectorAdd( newOrigin, viewDelta, newOrigin );
+ }
+
+ TracePlayerBBox( newOrigin, newOrigin, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+
+ if ( !trace.startsolid )
+ {
+ player->m_Local.m_bDucked = false;
+
+ // Oh, no, changing hulls stuck us into something, try unsticking downward first.
+ TracePlayerBBox( newOrigin, newOrigin, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+ if ( trace.startsolid )
+ {
+ // See if we are stuck? If so, stay ducked with the duck hull until we have a clear spot
+ player->m_Local.m_bDucked = true;
+ return;
+ }
+
+ player->RemoveFlag( FL_DUCKING );
+ player->m_Local.m_bDucking = false;
+ player->SetViewOffset( GetPlayerViewOffset( false ) );
+ player->m_Local.m_flDucktime = 0;
+
+ VectorCopy( newOrigin, mv->m_vecAbsOrigin );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::HandleDuck( void )
+{
+ // Store button presses and changes.
+ int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
+ int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed"
+ int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released"
+
+ if ( mv->m_nButtons & IN_DUCK )
+ {
+ mv->m_nOldButtons |= IN_DUCK;
+ }
+ else
+ {
+ mv->m_nOldButtons &= ~IN_DUCK;
+ }
+
+ // Handle the player dead case.
+ if ( IsDead() && ( player->GetFlags() & FL_DUCKING ) )
+ {
+ FinishUnDuck();
+ return;
+ }
+
+ if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
+ {
+ // Remove all movement when ducking!
+ mv->m_flForwardMove = 0.0f;
+ mv->m_flSideMove = 0.0f;
+ mv->m_flUpMove = 0.0f;
+
+ if ( mv->m_nButtons & IN_DUCK )
+ {
+ if ( ( buttonsPressed & IN_DUCK ) && !( player->GetFlags() & FL_DUCKING ) )
+ {
+ // Use 1 second so super long jump will work
+ player->m_Local.m_flDucktime = 1000;
+ player->m_Local.m_bDucking = true;
+ }
+
+ float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime );
+ float duckseconds = duckmilliseconds / 1000.0f;
+
+ if ( player->m_Local.m_bDucking )
+ {
+ // Finish ducking immediately if duck time is over or not on ground
+ if ( ( duckseconds > TIME_TO_DUCK ) ||
+ ( player->GetGroundEntity() == NULL ) )
+ {
+ FinishDuck();
+ }
+ else
+ {
+ // Calc parametric time
+ float duckFraction = SimpleSpline( duckseconds / TIME_TO_DUCK );
+ SetDuckedEyeOffset( duckFraction );
+ }
+ }
+ }
+ else
+ {
+ if ( (buttonsReleased & IN_DUCK ) && ( player->GetFlags() & FL_DUCKING ) )
+ {
+ // Use 1 second so super long jump will work
+ player->m_Local.m_flDucktime = 1000;
+ player->m_Local.m_bDucking = true; // or unducking
+ }
+
+ float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime );
+ float duckseconds = duckmilliseconds / 1000.0f;
+
+ if ( player->m_Local.m_bDucking ) // or unducking
+ {
+ // Finish ducking immediately if duck time is over or not on ground
+ if ( ( duckseconds > TIME_TO_UNDUCK ) ||
+ ( player->GetGroundEntity() == NULL ) )
+ {
+ FinishUnDuck();
+ }
+ else
+ {
+ // Calc parametric time
+ float duckFraction = SimpleSpline( 1.0f - ( duckseconds / TIME_TO_UNDUCK ) );
+ SetDuckedEyeOffset( duckFraction );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::SetupViewAngles( void )
+{
+ // Cache the view angles.
+ AngleVectors( mv->m_vecViewAngles, &m_vecForward, &m_vecRight, &m_vecUp );
+
+ // Add a view punch if necessary.
+ QAngle v_angle = ( mv->m_vecViewAngles + player->m_Local.m_vecPunchAngle );
+
+ // Adjust the view roll angle.
+ if ( ( player->GetMoveType() != MOVETYPE_ISOMETRIC ) && ( player->GetMoveType() != MOVETYPE_NOCLIP ) )
+ {
+ mv->m_vecViewAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() ) * 4.0f;
+ }
+ else
+ {
+ mv->m_vecViewAngles[ROLL] = 0.0f;
+ }
+
+ // Copy the yaw and pitch.
+ // NOTE: Adjust the client view angles to match the values on the server.
+ mv->m_vecViewAngles[PITCH] = v_angle[PITCH];
+ mv->m_vecViewAngles[YAW] = v_angle[YAW];
+ if ( mv->m_vecViewAngles[YAW] > 180.0f )
+ {
+ mv->m_vecViewAngles[YAW] -= 360.0f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovement::CheckDeath( void )
+{
+ // If we are dead, setup the appropriate data
+ if ( IsDead() )
+ {
+ mv->m_flForwardMove = 0.0f;
+ mv->m_flSideMove = 0.0f;
+ mv->m_flUpMove = 0.0f;
+
+ VectorCopy( mv->m_vecOldAngles, mv->m_vecViewAngles );
+
+ player->SetViewOffset( VEC_DEAD_VIEWHEIGHT_SCALED( this ) );
+
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::UpdateTimers( void )
+{
+ BaseClass::ReduceTimers();
+ BaseClass::DecayPunchAngle();
+}
+
+
+//-----------------------------------------------------------------------------
+// Should the step sound play?
+//-----------------------------------------------------------------------------
+bool CTFGameMovement::ShouldPlayStepSound( surfacedata_t *psurface, float fvol )
+{
+ if ( !m_nOnLadder && Vector2DLength( mv->m_vecVelocity.AsVector2D() ) <= 100 )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : step -
+// fvol -
+// force - force sound to play
+//-----------------------------------------------------------------------------
+void CTFGameMovement::PlayStepSound( surfacedata_t *psurface, float fvol, bool force )
+{
+ if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
+ return;
+
+ if ( !psurface )
+ return;
+
+ if (!force && !ShouldPlayStepSound( psurface, fvol ))
+ return;
+
+// TODO: See note above, should this be hooked up?
+// PlantFootprint( psurface );
+
+ unsigned short stepSoundName = player->m_Local.m_nStepside ? psurface->sounds.stepleft : psurface->sounds.stepright;
+ player->m_Local.m_nStepside = !player->m_Local.m_nStepside;
+
+ if ( !stepSoundName )
+ return;
+
+ if ( !mv->m_bFirstRunOfFunctions )
+ return;
+
+ IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
+ const char *pSoundName = physprops->GetString( stepSoundName );
+ char szSound[256];
+
+ // Prepend our team's footsteps
+ if ( player->GetTeamNumber() == TEAM_HUMANS )
+ {
+ Q_snprintf( szSound, sizeof(szSound), "Human.%s", pSoundName );
+ }
+ else if ( player->GetTeamNumber() == TEAM_ALIENS )
+ {
+ Q_snprintf( szSound, sizeof(szSound), "Alien.%s", pSoundName );
+ }
+ else
+ {
+ return;
+ }
+
+ CSoundParameters params;
+ if ( !CBaseEntity::GetParametersForSound( szSound, params, NULL ) )
+ return;
+
+ MoveHelper( )->StartSound( mv->m_vecAbsOrigin, CHAN_BODY, params.soundname, fvol, params.soundlevel, 0, params.pitch );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFGameMovement::CheckStuck( void )
+{
+ VPROF( "CTFGameMovement::CheckStuck" );
+
+ // NOTE: I am not bothering much with this as I am going to
+ // implement an new UnSticking process.
+ if ( ( player->GetMoveType() == MOVETYPE_NOCLIP ) ||
+ ( player->GetMoveType() == MOVETYPE_NONE ) ||
+ ( player->GetMoveType() == MOVETYPE_ISOMETRIC ) )
+ return 0;
+
+ return BaseClass::CheckStuck();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovement::PrePlayerMove( void )
+{
+ VPROF( "CTFGameMovement::PrePlayerMove" );
+
+ // Assume we don't touch anything (Reset the touch list).
+ MoveHelper()->ResetTouchList();
+
+ // Check to see if we are stuck.
+ if ( CheckStuck() )
+ return false;
+
+ // Update (reduce) movement timers.
+ UpdateTimers();
+
+ // Check to see if the player is dead and setup death data, otherwise setup
+ // the players view angles.
+ if ( !CheckDeath() )
+ {
+ SetupViewAngles();
+ }
+
+ // Handle ducking.
+ HandleDuck();
+
+ // Handle ladder.
+ HandleLadder();
+
+ // Categorize the player's position.
+ CategorizePosition();
+
+ // Calculate the player's movement speed (has to happen after categorize position)
+ SetupSpeed();
+
+ // Update our stepping sound (based on the player's location).
+ player->UpdateStepSound( m_pSurfaceData, mv->m_vecAbsOrigin, mv->m_vecVelocity );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::PostPlayerMove( void )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::HandlePlayerMove( void )
+{
+ // Handle movement.
+ switch ( player->GetMoveType() )
+ {
+ case MOVETYPE_NONE:
+ {
+ break;
+ }
+ case MOVETYPE_NOCLIP:
+ {
+ FullNoClipMove( sv_noclipspeed.GetFloat(), sv_noclipaccelerate.GetFloat() );
+ break;
+ }
+ case MOVETYPE_FLY:
+ case MOVETYPE_FLYGRAVITY:
+ {
+ FullTossMove();
+ break;
+ }
+
+ case MOVETYPE_LADDER:
+ {
+ FullLadderMove();
+ break;
+ }
+
+ case MOVETYPE_WALK:
+ {
+ // This should be moved elsewhere!!! Just get it going for now.
+ CTFMoveData *pTFMove = TFMove();
+ Vector vecPlayerOrigin( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z );
+ FullWalkMove();
+ Vector vecPlayerDelta;
+ VectorSubtract( mv->m_vecAbsOrigin, vecPlayerOrigin, vecPlayerDelta );
+
+ if ( ( fabs( vecPlayerDelta.x ) > 0.0001f ) ||
+ ( fabs( vecPlayerDelta.y ) > 0.0001f ) ||
+ ( fabs( vecPlayerDelta.z ) > 0.0001f ) )
+ {
+ VectorCopy( vecPlayerDelta, pTFMove->m_vecPosDelta );
+ }
+ break;
+ }
+
+ case MOVETYPE_ISOMETRIC:
+ {
+ //IsometricMove();
+ // Could also try: FullTossMove();
+ FullWalkMove();
+ break;
+ }
+
+ default:
+ {
+ DevMsg( 1, "Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", player->GetMoveType(), player->IsServer());
+ break;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::PlayerMove( void )
+{
+ VPROF( "CTFGameMovement::PlayerMove" );
+
+ // Setup and initialization pre-player movement.
+ if (!PrePlayerMove())
+ return;
+
+ // Handle Movement
+ HandlePlayerMove();
+
+ // Clean-up and updates post-player movement.
+ PostPlayerMove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::FullWalkMove()
+{
+ VPROF( "CTFGameMovement::FullWalkMove" );
+
+ if ( !CheckWater() )
+ {
+ StartGravity();
+ }
+
+ // If we are leaping out of the water, just update the counters.
+ if (player->m_flWaterJumpTime)
+ {
+ WaterJump();
+ TryPlayerMove();
+ // See if we are still in water?
+ CheckWater();
+ return;
+ }
+
+ // If we are swimming in the water, see if we are nudging against a place we can jump up out
+ // of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0
+ if ( player->GetWaterLevel() >= WL_Waist )
+ {
+ if ( player->GetWaterLevel() == WL_Waist )
+ {
+ CheckWaterJump();
+ }
+
+ // If we are falling again, then we must not trying to jump out of water any more.
+ if ( mv->m_vecVelocity[2] < 0 &&
+ player->m_flWaterJumpTime )
+ {
+ player->m_flWaterJumpTime = 0;
+ }
+
+ // Was jump button pressed?
+ if (mv->m_nButtons & IN_JUMP)
+ {
+ CheckJumpButton();
+ }
+ else
+ {
+ mv->m_nOldButtons &= ~IN_JUMP;
+ }
+
+ // Perform regular water movement
+ WaterMove();
+ VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
+
+ // Redetermine position vars
+ CategorizePosition();
+ }
+ else
+ // Not fully underwater
+ {
+ // Was jump button pressed?
+ if (mv->m_nButtons & IN_JUMP)
+ {
+ CheckJumpButton();
+ }
+ else
+ {
+ mv->m_nOldButtons &= ~IN_JUMP;
+ }
+
+ // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor,
+ // we don't slow when standing still, relative to the conveyor.
+ if (player->GetGroundEntity() != NULL)
+ {
+ mv->m_vecVelocity[2] = 0.0;
+ Friction();
+ }
+
+ // Make sure velocity is valid.
+ CheckVelocity();
+
+ if (player->GetGroundEntity() != NULL)
+ {
+// WalkMove();
+ WalkMove2();
+ }
+ else
+ {
+ AirMove(); // Take into account movement when in air.
+ }
+
+ // Set final flags.
+ CategorizePosition();
+
+ // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like
+ // a conveyor (or maybe another monster?)
+ VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+
+ // Make sure velocity is valid.
+ CheckVelocity();
+
+ // Add any remaining gravitational component.
+ if ( !CheckWater() )
+ {
+ FinishGravity();
+ }
+
+ // If we are on ground, no downward velocity.
+ if ( player->GetGroundEntity() != NULL )
+ {
+ mv->m_vecVelocity[2] = 0;
+ }
+ CheckFalling();
+ }
+
+ if ( ( m_nOldWaterLevel == WL_NotInWater && player->GetWaterLevel() != WL_NotInWater ) ||
+ ( m_nOldWaterLevel != WL_NotInWater && player->GetWaterLevel() == WL_NotInWater ) )
+ {
+ PlaySwimSound();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::AirMove( void )
+{
+ VPROF( "CTFGameMovement::AirMove" );
+
+ // NOTE: This was causing some additional problems with walking on physics
+ // objects. The movement system needs to be cleaned up! Keep the old
+ // code until the system has been fixed.s
+ BaseClass::AirMove();
+ return;
+
+
+ int i;
+ Vector wishvel;
+ float fmove, smove;
+ Vector wishdir;
+ float wishspeed;
+ Vector forward, right, up;
+
+ // Initialize the movement stack.
+ VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[0].m_vecPosition );
+ VectorCopy( m_vecGroundNormal, m_aMovementStack[0].m_vecImpactNormal );
+ m_nMovementStackSize = 1;
+
+ AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
+
+ // Copy movement amounts
+ fmove = mv->m_flForwardMove;
+ smove = mv->m_flSideMove;
+
+ // Zero out z components of movement vectors
+ forward[2] = 0;
+ right[2] = 0;
+ VectorNormalize(forward); // Normalize remainder of vectors
+ VectorNormalize(right); //
+
+ for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
+ wishvel[i] = forward[i]*fmove + right[i]*smove;
+ wishvel[2] = 0; // Zero out z part of velocity
+
+ VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
+ wishspeed = VectorNormalize(wishdir);
+
+ //
+ // clamp to server defined max speed
+ //
+ if (wishspeed > mv->m_flMaxSpeed)
+ {
+ VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
+ wishspeed = mv->m_flMaxSpeed;
+ }
+
+ AirAccelerate( wishdir, wishspeed, sv_airaccelerate.GetFloat() );
+
+ // Add in any base velocity to the current velocity.
+ VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+
+ TryPlayerMove2();
+
+ // Try to stand up.
+ TryStanding();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::WalkMove( void )
+{
+ _WalkMove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: This is here until the new movement code is complete. I needed
+// to override hl2's walk move so that "floors" are identified
+// differently.
+//-----------------------------------------------------------------------------
+void CTFGameMovement::_WalkMove( void )
+{
+ VPROF( "CTFGameMovement::_WalkMove" );
+
+ int clip;
+ int i;
+
+ Vector wishvel;
+ float spd;
+ float fmove, smove;
+ Vector wishdir;
+ float wishspeed;
+
+ Vector dest, start;
+ Vector original, originalvel;
+ Vector down, downvel;
+ float downdist, updist;
+ trace_t pm;
+ Vector forward, right, up;
+
+ AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
+
+ CHandle< CBaseEntity > oldground;
+ oldground = player->GetGroundEntity();
+
+ // Copy movement amounts
+ fmove = mv->m_flForwardMove;
+ smove = mv->m_flSideMove;
+
+ // Zero out z components of movement vectors
+ forward[2] = 0;
+ right[2] = 0;
+
+ VectorNormalize (forward); // Normalize remainder of vectors.
+ VectorNormalize (right); //
+
+ for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
+ wishvel[i] = forward[i]*fmove + right[i]*smove;
+
+ wishvel[2] = 0; // Zero out z part of velocity
+
+ VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
+ wishspeed = VectorNormalize(wishdir);
+
+ //
+ // Clamp to server defined max speed
+ //
+ if (wishspeed > mv->m_flMaxSpeed)
+ {
+ VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
+ wishspeed = mv->m_flMaxSpeed;
+ }
+
+ // Set pmove velocity
+ mv->m_vecVelocity[2] = 0;
+ Accelerate ( wishdir, wishspeed, sv_accelerate.GetFloat() );
+ mv->m_vecVelocity[2] = 0;
+
+ // Add in any base velocity to the current velocity.
+ VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+
+ spd = VectorLength( mv->m_vecVelocity );
+
+ if (spd < 1.0f)
+ {
+ mv->m_vecVelocity.Init();
+ return;
+ }
+
+ // first try just moving to the destination
+ dest[0] = mv->m_vecAbsOrigin[0] + mv->m_vecVelocity[0]*gpGlobals->frametime;
+ dest[1] = mv->m_vecAbsOrigin[1] + mv->m_vecVelocity[1]*gpGlobals->frametime;
+ dest[2] = mv->m_vecAbsOrigin[2];
+
+ // first try moving directly to the next spot
+ VectorCopy (dest, start);
+
+ TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );
+
+ // If we made it all the way, then copy trace end
+ // as new player position.
+
+ mv->m_outWishVel += wishdir * wishspeed;
+
+ if (pm.fraction == 1)
+ {
+ VectorCopy( pm.endpos, mv->m_vecAbsOrigin );
+ return;
+ }
+
+ if (oldground == NULL && // Don't walk up stairs if not on ground.
+ player->GetWaterLevel() == 0)
+ return;
+
+ if (player->m_flWaterJumpTime) // If we are jumping out of water, don't do anything more.
+ return;
+
+ // Try sliding forward both on ground and up 16 pixels
+ // take the move that goes farthest
+ VectorCopy (mv->m_vecAbsOrigin, original); // Save out original pos &
+ VectorCopy (mv->m_vecVelocity, originalvel); // velocity.
+
+ // Slide move
+ clip = TryPlayerMove();
+
+ // Copy the results out
+ VectorCopy (mv->m_vecAbsOrigin , down);
+ VectorCopy (mv->m_vecVelocity, downvel);
+
+ // Reset original values.
+ VectorCopy (original, mv->m_vecAbsOrigin);
+ VectorCopy (originalvel, mv->m_vecVelocity);
+
+ // Start out up one stair height
+ VectorCopy (mv->m_vecAbsOrigin, dest);
+ dest[2] += player->m_Local.m_flStepSize;
+
+ TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );
+
+ // If we started okay and made it part of the way at least,
+ // copy the results to the movement start position and then
+ // run another move try.
+ if (!pm.startsolid && !pm.allsolid)
+ {
+ VectorCopy (pm.endpos, mv->m_vecAbsOrigin);
+ }
+
+ // slide move the rest of the way.
+ clip = TryPlayerMove();
+
+ // Now try going back down from the end point
+ // press down the stepheight
+ VectorCopy (mv->m_vecAbsOrigin, dest);
+ dest[2] -= player->m_Local.m_flStepSize;
+
+ TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );
+
+ // If we are not on the ground any more then
+ // use the original movement attempt
+ if ( pm.plane.normal[2] < IMPACT_NORMAL_FLOOR )
+ goto usedown;
+ // If the trace ended up in empty space, copy the end
+ // over to the origin.
+ if (!pm.startsolid && !pm.allsolid)
+ {
+ VectorCopy (pm.endpos, mv->m_vecAbsOrigin);
+ }
+ // Copy this origion to up.
+ VectorCopy (mv->m_vecAbsOrigin, up);
+
+ // decide which one went farther
+ downdist = (down[0] - original[0])*(down[0] - original[0])
+ + (down[1] - original[1])*(down[1] - original[1]);
+ updist = (up[0] - original[0])*(up[0] - original[0])
+ + (up[1] - original[1])*(up[1] - original[1]);
+
+ if (downdist > updist)
+ {
+usedown:
+ ;
+ VectorCopy (down , mv->m_vecAbsOrigin);
+ VectorCopy (downvel, mv->m_vecVelocity);
+ }
+ else // copy z value from slide move
+ mv->m_vecVelocity[2] = downvel[2];
+
+ float stepDist = mv->m_vecAbsOrigin.z - original.z;
+ if ( stepDist > 0 )
+ {
+ mv->m_outStepHeight += stepDist;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::AccelerateWithoutMomentum( Vector &wishdir, float wishspeed, float accel )
+{
+ // No acceleration if the player is water-jumping or dead.
+ if ( player->pl.deadflag || player->m_flWaterJumpTime )
+ return;
+
+ // See if we are changing direction a bit
+// float flCurrentSpeed = mv->m_vecVelocity.Dot( wishdir );
+ float flCurrentSpeed = mv->m_vecVelocity.Length();
+
+ // Reduce wishspeed by the amount of veer.
+ float flAddSpeed = wishspeed - flCurrentSpeed;
+
+ // If not going to add any speed, done.
+ if ( flAddSpeed <= 0.0f )
+ return;
+
+ // Determine amount of accleration.
+ float flAccelSpeed = accel * gpGlobals->frametime * wishspeed * m_surfaceFriction;
+
+ // Cap at addspeed
+ if ( flAccelSpeed > flAddSpeed )
+ {
+ flAccelSpeed = flAddSpeed;
+ }
+
+ // Adjust velocity.
+ for ( int iAxis = 0; iAxis < 3; iAxis++ )
+ {
+ mv->m_vecVelocity[iAxis] += flAccelSpeed * wishdir[iAxis];
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::Accelerate( Vector &wishdir, float wishspeed, float accel )
+{
+ // No acceleration if the player is water-jumping or dead.
+ if ( player->pl.deadflag || player->m_flWaterJumpTime )
+ return;
+
+ // See if we are changing direction a bit
+// float flCurrentSpeed = mv->m_vecVelocity.Dot( wishdir );
+ float flCurrentSpeed = mv->m_vecVelocity.Length();
+
+ // Reduce wishspeed by the amount of veer.
+ float flAddSpeed = wishspeed - flCurrentSpeed;
+
+ // If not going to add any speed, done.
+ if ( flAddSpeed <= 0.0f )
+ return;
+
+ // Determine amount of accleration.
+ float flAccelSpeed = accel * gpGlobals->frametime * wishspeed * m_surfaceFriction;
+
+ // Cap at addspeed
+ if ( flAccelSpeed > flAddSpeed )
+ {
+ flAccelSpeed = flAddSpeed;
+ }
+
+ // Gravity.
+ float flGravityAdj = CalcGravityAdjustment( wishdir );
+
+ // Adjust velocity.
+ for ( int iAxis = 0; iAxis < 3; iAxis++ )
+ {
+ mv->m_vecVelocity[iAxis] += ( flAccelSpeed * wishdir[iAxis] * flGravityAdj );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFGameMovement::CalcGravityAdjustment( const Vector &wishdir )
+{
+ // Get the TF movement data.
+ CTFMoveData *pTFMove = TFMove();
+ if ( !pTFMove )
+ return 1.0f;
+
+ if ( player->GetMoveType() == MOVETYPE_NOCLIP )
+ return 1.0f;
+
+ Vector vecPositionDelta = pTFMove->m_vecPosDelta;
+ VectorNormalize( vecPositionDelta );
+
+ // Hard-code my table
+ float flModifier = 0.0f;
+ if ( vecPositionDelta.z < -0.342f )
+ {
+ flModifier = 1.25f;
+ }
+ else if ( vecPositionDelta.z < 0.0f )
+ {
+ flModifier = 1.15f;
+ }
+ else if ( vecPositionDelta.z < 0.342f ) /* >20 */
+ {
+ flModifier = 1.0f;
+ }
+ else if ( vecPositionDelta.z < 0.5f ) /* 20-30 */
+ {
+ float flDelta = 0.5f - vecPositionDelta.z;
+ flDelta /= 0.158f;
+ flDelta = 1.0f - flDelta;
+ flModifier = 1.0f - ( 0.25 * flDelta );
+ }
+ else if ( vecPositionDelta.z < 0.707f ) /* 30-45 */
+ {
+ float flDelta = 0.707f - vecPositionDelta.z;
+ flDelta /= 0.207f;
+ flDelta = 1.0f - flDelta;
+ flModifier = 0.75 - ( 0.25f * flDelta );
+ }
+ else if ( vecPositionDelta.z < 0.766f ) /* 45-50 */
+ {
+ float flDelta = 0.766f - vecPositionDelta.z;
+ flDelta /= 0.059f;
+ flDelta = 1.0f - flDelta;
+ flModifier = 0.5f - ( 0.40f * flDelta );
+ }
+ else
+ {
+ flModifier = 0.35f;
+ }
+
+ AddToMomentumList( flModifier );
+ flModifier = GetMomentum();
+
+#if 0
+ // Debug!
+ if ( player->IsServer() )
+ {
+ Msg( "Server:Gravity Adj = %lf\n", flModifier );
+ }
+ else
+ {
+ Msg( "Client:Gravity Adj = %lf\n", flModifier );
+ }
+#endif
+
+ return flModifier;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovement::CalcWishVelocityAndPosition( Vector &vWishPos, Vector &vWishDir,
+ float &flWishSpeed )
+{
+ //
+ // Determine the movement angles.
+ //
+ Vector vForward, vRight, vUp;
+ AngleVectors( mv->m_vecViewAngles, &vForward, &vRight, &vUp );
+
+ //
+ // Zero out the z component of the movement vectors and renormalize.
+ //
+ vForward.z = 0.0f;
+ VectorNormalize( vForward );
+ vRight.z = 0.0f;
+ VectorNormalize( vRight );
+
+ //
+ // Determine the xy parts of the velocity.
+ //
+ Vector vWishVel( 0.0f, 0.0f, 0.0f );
+ for ( int axis = 0; axis < 2; axis++ )
+ {
+ vWishVel[axis] = ( vForward[axis] * mv->m_flForwardMove ) +
+ ( vRight[axis] * mv->m_flSideMove );
+ }
+ vWishVel.z = 0.0f;
+
+ //
+ // Componentize the velocity into direction and speed.
+ //
+ VectorCopy( vWishVel, vWishDir );
+ flWishSpeed = VectorNormalize( vWishDir );
+ if ( flWishSpeed > mv->m_flMaxSpeed )
+ {
+ VectorScale( vWishVel, ( mv->m_flMaxSpeed / flWishSpeed ), vWishVel );
+ flWishSpeed = mv->m_flMaxSpeed;
+ }
+
+ //
+ // Accelerate (in the plane).
+ //
+ mv->m_vecVelocity.z = 0.0f;
+ Accelerate( vWishDir, flWishSpeed, sv_accelerate.GetFloat() );
+ mv->m_vecVelocity.z = 0.0f;
+
+ // Add in any base velocity (from conveyers, etc.) to the current velocity.
+ VectorAdd( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+
+ //
+ // Stop the player (zero out velocity) if the player's speed is below a
+ // given threshold.
+ //
+ float flSpeed = VectorLength( mv->m_vecVelocity );
+ if ( flSpeed < SPEED_STOP_THRESHOLD )
+ {
+ mv->m_vecVelocity.Init();
+ return false;
+ }
+
+ //
+ // Calculate the wish position.
+ //
+ vWishPos.x = mv->m_vecAbsOrigin.x + ( mv->m_vecVelocity.x * gpGlobals->frametime );
+ vWishPos.y = mv->m_vecAbsOrigin.y + ( mv->m_vecVelocity.y * gpGlobals->frametime );
+ vWishPos.z = mv->m_vecAbsOrigin.z;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline void CTFGameMovement::TracePlayerBBoxWithStep( const Vector &vStart, const Vector &vEnd,
+ unsigned int fMask, int collisionGroup, trace_t &trace )
+{
+ VPROF( "CTFGameMovement::TracePlayerBBoxWithStep" );
+
+ Vector vHullMin = GetPlayerMins( player->m_Local.m_bDucked );
+ vHullMin.z += player->m_Local.m_flStepSize;
+ Vector vHullMax = GetPlayerMaxs( player->m_Local.m_bDucked );
+
+ Ray_t ray;
+ ray.Init( vStart, vEnd, vHullMin, vHullMax );
+ UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &trace );
+}
+
+#if 0
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline void CGameMovement::TracePlayerBBox( const Vector &start, const Vector &end,
+ unsigned int fMask, int collisionGroup, trace_t& pm )
+{
+ VPROF( "CTFGameMovement::TracePlayerBBox" );
+
+ Ray_t ray;
+ ray.Init( start, end, GetPlayerMins( player->m_Local.m_bDucked ), GetPlayerMaxs( player->m_Local.m_bDucked ) );
+ UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+inline int CTFGameMovement::BlockerType( const Vector &vImpactNormal )
+{
+ // If the impact plane has a high z component in the normal, then
+ // it is probably a floor.
+ if ( vImpactNormal.z > IMPACT_NORMAL_FLOOR )
+ return 1;
+
+ // If the impact plane has a zero z component in the normal, then it is
+ // probably a step or wall.
+ if ( vImpactNormal.z == IMPACT_NORMAL_WALL )
+ return 2;
+
+ // None
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovement::RedirectGroundVelocity( const trace_t &trace )
+{
+ // Check for max planes.
+ if ( m_nImpactPlaneCount >= MAX_IMPACT_PLANES )
+ {
+ mv->m_vecVelocity.Init();
+ return false;
+ }
+
+ // Add the impact plane normal to the list.
+ VectorCopy( trace.plane.normal, m_aImpactPlaneNormals[m_nImpactPlaneCount] );
+ m_nImpactPlaneCount++;
+
+ int iPlane, iPlane2;
+ for ( iPlane = 0; iPlane < m_nImpactPlaneCount; iPlane++ )
+ {
+ // Reduce and redirect the player's velocity.
+ Vector vecVelocity( mv->m_vecVelocity.x, mv->m_vecVelocity.y, mv->m_vecVelocity.z );
+
+ // Don't let negatively sloped surfaces drive you into the ground.
+ if ( m_aImpactPlaneNormals[iPlane].z < 0.0f )
+ {
+ m_aImpactPlaneNormals[iPlane].z = 0.0f;
+ VectorNormalize( m_aImpactPlaneNormals[iPlane] );
+ }
+
+ ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f );
+
+ // Check to see if we need to continue clipping?
+ for( iPlane2 = 0; iPlane2 < m_nImpactPlaneCount; iPlane2++ )
+ {
+ if ( iPlane != iPlane2 )
+ {
+ if ( mv->m_vecVelocity.Dot( m_aImpactPlaneNormals[iPlane2] ) < 0.0f )
+ break;
+ }
+ }
+ if ( iPlane2 == m_nImpactPlaneCount )
+ break;
+ }
+
+ if ( iPlane == m_nImpactPlaneCount )
+ {
+ // Go along the crease here!
+ if ( m_nImpactPlaneCount != 2 )
+ {
+ mv->m_vecVelocity.Init();
+ return false;
+ }
+ else
+ {
+ Vector vecCross;
+ CrossProduct( m_aImpactPlaneNormals[0], m_aImpactPlaneNormals[1], vecCross );
+ float flScalar = vecCross.Dot( mv->m_vecVelocity );
+ VectorScale( vecCross, flScalar, mv->m_vecVelocity );
+ }
+ }
+
+ // If the original velocity is against the current velocity, stop dead to
+ // avoid tiny occilations in sloping corners
+ if ( mv->m_vecVelocity.Dot( m_vecOriginalVelocity ) <= 0.0f )
+ {
+ mv->m_vecVelocity.Init();
+ return false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovement::RedirectAirVelocity( const trace_t &trace )
+{
+ // Check for max planes.
+ if ( m_nImpactPlaneCount >= MAX_IMPACT_PLANES )
+ {
+ mv->m_vecVelocity.Init();
+ return false;
+ }
+
+ // Add the impact plane normal to the list.
+ VectorCopy( trace.plane.normal, m_aImpactPlaneNormals[m_nImpactPlaneCount] );
+ m_nImpactPlaneCount++;
+
+ for ( int iPlane = 0; iPlane < m_nImpactPlaneCount; iPlane++ )
+ {
+ // Reduce and redirect the player's velocity.
+ Vector vecVelocity( mv->m_vecVelocity.x, mv->m_vecVelocity.y, mv->m_vecVelocity.z );
+
+ // Don't let negatively sloped surfaces drive you into the ground.
+ if ( m_aImpactPlaneNormals[iPlane].z < 0.0f )
+ {
+ m_aImpactPlaneNormals[iPlane].z = 0.0f;
+ VectorNormalize( m_aImpactPlaneNormals[iPlane] );
+ }
+
+ if ( m_aImpactPlaneNormals[iPlane].z > IMPACT_NORMAL_FLOOR )
+ {
+ ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f );
+ }
+ else
+ {
+ ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f + sv_bounce.GetFloat() * ( 1.0f - m_surfaceFriction ) );
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::CollisionResponseNone( const trace_t &trace )
+{
+ // Move the player to the trace's ending position.
+ VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
+
+ // Add the position to the stack.
+ if ( m_nMovementStackSize < MOVEMENTSTACK_MAXSIZE )
+ {
+ VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[m_nMovementStackSize].m_vecPosition );
+ VectorCopy( mv->m_vecVelocity, m_aMovementStack[m_nMovementStackSize].m_vecVelocity );
+ VectorCopy( m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal,
+ m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal );
+ m_nMovementStackSize++;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::CollisionResponseStuck( void )
+{
+ mv->m_vecVelocity.Init();
+ //DevMsg( 1, "CollisionResponseStuck: %s is stuck (%s).\n", player->GetClassname(), player->IsServer() ? "sv" : "cl" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovement::CollisionResponseGeneric( const trace_t &trace, int &nBlocked )
+{
+ // Check for any movement.
+ if ( trace.fraction > 0.0f )
+ {
+ // Move the partial move and reset the impact plane count.
+ VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
+ m_nImpactPlaneCount = 0;
+
+ // Add the data to the movement stack. -- the velocity is wrong here!!!
+ if ( m_nMovementStackSize < MOVEMENTSTACK_MAXSIZE )
+ {
+ VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[m_nMovementStackSize].m_vecPosition );
+ VectorCopy( mv->m_vecVelocity, m_aMovementStack[m_nMovementStackSize].m_vecVelocity );
+ VectorCopy( trace.plane.normal, m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal );
+ m_nMovementStackSize++;
+ }
+ }
+
+ // Sanity check - Impact plane count.
+ Assert( m_nImpactPlaneCount < MAX_IMPACT_PLANES );
+
+ // Add the entity to the touched list (list itself maintains uniqueness).
+ MoveHelper()->AddToTouched( trace, mv->m_vecVelocity );
+
+ // Check for special "blockers" (walls, steps, floor).
+ nBlocked |= BlockerType( trace.plane.normal );
+
+ // Re-direct or bounce the player's velocity vector.
+ if ( player->GetGroundEntity() != NULL )
+ {
+ return RedirectGroundVelocity( trace );
+ }
+ else
+ {
+ return RedirectAirVelocity( trace );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: See comment _WalkMove
+//-----------------------------------------------------------------------------
+int CTFGameMovement::TryPlayerMove( Vector *pFirstDest=NULL, trace_t *pFirstTrace=NULL )
+{
+ VPROF( "CTFGameMovement::TryPlayerMove" );
+
+ int bumpcount, numbumps;
+ Vector dir;
+ float d;
+ int numplanes;
+ Vector planes[5/*MAX_CLIP_PLANES*/];
+ Vector primal_velocity, original_velocity;
+ Vector new_velocity;
+ int i, j;
+ trace_t pm;
+ Vector end;
+ float time_left, allFraction;
+ int blocked;
+
+ numbumps = 4; // Bump up to four times
+
+ blocked = 0; // Assume not blocked
+ numplanes = 0; // and not sliding along any planes
+ VectorCopy (mv->m_vecVelocity, original_velocity); // Store original velocity
+ VectorCopy (mv->m_vecVelocity, primal_velocity);
+
+ allFraction = 0;
+ time_left = gpGlobals->frametime; // Total time for this movement operation.
+
+ new_velocity.Init();
+
+ for (bumpcount=0 ; bumpcount < numbumps; bumpcount++)
+ {
+ if ( mv->m_vecVelocity.Length() == 0.0 )
+ break;
+
+ // Assume we can move all the way from the current origin to the
+ // end point.
+ VectorMA( mv->m_vecAbsOrigin, time_left, mv->m_vecVelocity, end );
+
+ // See if we can make it from origin to end point.
+ if ( g_bMovementOptimizations )
+ {
+ // If their velocity Z is 0, then we can avoid an extra trace here during WalkMove.
+ if ( pFirstDest && end == *pFirstDest )
+ pm = *pFirstTrace;
+ else
+ TracePlayerBBox( mv->m_vecAbsOrigin, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
+ }
+ else
+ {
+ TracePlayerBBox( mv->m_vecAbsOrigin, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
+ }
+
+ allFraction += pm.fraction;
+
+ // If we started in a solid object, or we were in solid space
+ // the whole way, zero out our velocity and return that we
+ // are blocked by floor and wall.
+ if (pm.allsolid)
+ {
+ // entity is trapped in another solid
+ VectorCopy (vec3_origin, mv->m_vecVelocity);
+ return 4;
+ }
+
+ // If we moved some portion of the total distance, then
+ // copy the end position into the pmove.origin and
+ // zero the plane counter.
+ if( pm.fraction > 0 )
+ {
+ // actually covered some distance
+ VectorCopy (pm.endpos, mv->m_vecAbsOrigin);
+ VectorCopy (mv->m_vecVelocity, original_velocity);
+ numplanes = 0;
+ }
+
+ // If we covered the entire distance, we are done
+ // and can return.
+ if (pm.fraction == 1)
+ {
+ break; // moved the entire distance
+ }
+
+ // Indicate a collision occurred...
+ OnTryPlayerMoveCollision( pm );
+
+ // Save entity that blocked us (since fraction was < 1.0)
+ // for contact
+ // Add it if it's not already in the list!!!
+ MoveHelper( )->AddToTouched( pm, mv->m_vecVelocity );
+
+ // If the plane we hit has a high z component in the normal, then
+ // it's probably a floor
+ if (pm.plane.normal[2] > IMPACT_NORMAL_FLOOR)
+ {
+ blocked |= 1; // floor
+ }
+ // If the plane has a zero z component in the normal, then it's a
+ // step or wall
+ if (!pm.plane.normal[2])
+ {
+ blocked |= 2; // step / wall
+ }
+
+ // Reduce amount of m_flFrameTime left by total time left * fraction
+ // that we covered.
+ time_left -= time_left * pm.fraction;
+
+ // Did we run out of planes to clip against?
+ if (numplanes >= 5/*MAX_CLIP_PLANES*/)
+ {
+ // this shouldn't really happen
+ // Stop our movement if so.
+ VectorCopy (vec3_origin, mv->m_vecVelocity);
+ //Con_DPrintf("Too many planes 4\n");
+
+ break;
+ }
+
+ // Set up next clipping plane
+ VectorCopy (pm.plane.normal, planes[numplanes]);
+ numplanes++;
+
+ // modify original_velocity so it parallels all of the clip planes
+ //
+ if ( player->GetMoveType() == MOVETYPE_WALK &&
+ ((player->GetGroundEntity() == NULL) /*|| (mv->m_fFriction != 1)*/ ) ) // relfect player velocity
+ {
+ for ( i = 0; i < numplanes; i++ )
+ {
+ if ( planes[i][2] > IMPACT_NORMAL_FLOOR )
+ {
+ // floor or slope
+ ClipVelocity( original_velocity, planes[i], new_velocity, 1 );
+ VectorCopy( new_velocity, original_velocity );
+ }
+ else
+ {
+ ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + sv_bounce.GetFloat() * (1 - m_surfaceFriction) );
+ }
+ }
+
+ VectorCopy( new_velocity, mv->m_vecVelocity );
+ VectorCopy( new_velocity, original_velocity );
+ }
+ else
+ {
+ for (i=0 ; i < numplanes ; i++)
+ {
+ ClipVelocity (
+ original_velocity,
+ planes[i],
+ mv->m_vecVelocity,
+ 1);
+
+ for (j=0 ; j<numplanes ; j++)
+ if (j != i)
+ {
+ // Are we now moving against this plane?
+ if (mv->m_vecVelocity.Dot(planes[j]) < 0)
+ break; // not ok
+ }
+ if (j == numplanes) // Didn't have to clip, so we're ok
+ break;
+ }
+
+ // Did we go all the way through plane set
+ if (i != numplanes)
+ { // go along this plane
+ // pmove.velocity is set in clipping call, no need to set again.
+ ;
+ }
+ else
+ { // go along the crease
+ if (numplanes != 2)
+ {
+ VectorCopy (vec3_origin, mv->m_vecVelocity);
+ break;
+ }
+ CrossProduct (planes[0], planes[1], dir);
+ d = dir.Dot(mv->m_vecVelocity);
+ VectorScale (dir, d, mv->m_vecVelocity );
+ }
+
+ //
+ // if original velocity is against the original velocity, stop dead
+ // to avoid tiny occilations in sloping corners
+ //
+ d = mv->m_vecVelocity.Dot(primal_velocity);
+ if (d <= 0)
+ {
+ //Con_DPrintf("Back\n");
+ VectorCopy (vec3_origin, mv->m_vecVelocity);
+ break;
+ }
+ }
+ }
+
+ if ( allFraction == 0 )
+ {
+ VectorCopy (vec3_origin, mv->m_vecVelocity);
+ }
+
+ return blocked;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFGameMovement::TryPlayerMove2( void )
+{
+ VPROF( "CTFGameMovement::TryPlayerMove2" );
+
+ int nBlocked = MOVEMENT_BLOCKED_NONE;
+ trace_t trace;
+ float flTimeLeft = gpGlobals->frametime;
+ m_nImpactPlaneCount = 0;
+
+ // Save off the original velocity -- coming in.
+ VectorCopy( mv->m_vecVelocity, m_vecOriginalVelocity );
+
+ float flTotalFraction = 0.0f;
+ for ( int iBump = 0; iBump < BUMP_MAX_COUNT; iBump++ )
+ {
+ // Check to see if we have any velocity left. EXPENSIVE!!!!
+ if ( mv->m_vecVelocity.Length() == 0.0f )
+ break;
+
+ //
+ // Calculate the wish position.
+ //
+ Vector vecWishPos;
+ VectorMA( mv->m_vecAbsOrigin, flTimeLeft, mv->m_vecVelocity, vecWishPos );
+
+ //
+ // Attempt the move.
+ //
+ // NOTE: This check can hit players and objects
+ TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER, trace );
+
+ // but if it didn't no need to do another trace for movement only
+ if ( trace.fraction == 1.0f )
+ {
+ flTotalFraction += trace.fraction;
+ CollisionResponseNone( trace );
+ break;
+ }
+ else
+ {
+ // Add the entity to the touched list (list itself maintains uniqueness).
+ MoveHelper()->AddToTouched( trace, mv->m_vecVelocity );
+ // Do a non-solid to player trace now, instead, and override what's in trace
+ TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+ }
+
+ flTotalFraction += trace.fraction;
+
+ // Handle stuck in solid.
+ if ( trace.allsolid )
+ {
+ CollisionResponseStuck();
+ return MOVEMENT_BLOCKED_ALL;
+ }
+
+ // Handle full movement.
+ if ( trace.fraction == 1.0f )
+ {
+ CollisionResponseNone( trace );
+ break;
+ }
+
+ // Handle partial movement.
+ if ( !CollisionResponseGeneric( trace, nBlocked ) )
+ break;
+
+ // Reduce the time left.
+ flTimeLeft -= ( flTimeLeft * trace.fraction );
+
+ /*
+ //
+ // Attempt the move.
+ //
+ TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+
+ flTotalFraction += trace.fraction;
+
+ // Handle stuck in solid.
+ if ( trace.allsolid )
+ {
+ CollisionResponseStuck();
+ return MOVEMENT_BLOCKED_ALL;
+ }
+
+ // Handle full movement.
+ if ( trace.fraction == 1.0f )
+ {
+ CollisionResponseNone( trace );
+ break;
+ }
+
+ // Handle partial movement.
+ if ( !CollisionResponseGeneric( trace, nBlocked ) )
+ break;
+
+ // Reduce the time left.
+ flTimeLeft -= ( flTimeLeft * trace.fraction );
+ */
+ }
+
+ // Finally, check for any movement.
+ if ( flTotalFraction == 0.0f )
+ {
+ CollisionResponseStuck();
+ }
+
+ return nBlocked;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::TryStanding( void )
+{
+ VPROF( "CTFGameMovement::TryStanding" );
+
+ // Step down.
+ Vector vecStandPos( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - player->m_Local.m_flStepSize );
+
+ trace_t trace;
+ TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+ if ( trace.fraction == 1.0f )
+ return;
+
+ // Attempt to stand up.
+ vecStandPos.z = mv->m_vecAbsOrigin.z + ( ( 1.0f - trace.fraction ) * player->m_Local.m_flStepSize );
+ TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+
+ // A fraction of 1.0 means we stood up fine - done.
+ if ( trace.fraction == 1.0f )
+ {
+ VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
+ return;
+ }
+
+ // Use the movement stack to pop back and resolve the stand.
+ if ( m_nMovementStackSize > 0 )
+ {
+ VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecPosition, mv->m_vecAbsOrigin );
+ VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecVelocity, mv->m_vecVelocity );
+ m_nMovementStackSize--;
+ TryStanding();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::ResolveStanding( void )
+{
+ VPROF( "CTFGameMovement::ResolveStanding" );
+
+ //
+ // Attempt to move down twice your step height. Anything between 0.5 and 1.0
+ // is a valid "stand" value.
+ //
+ Vector vecStandPos( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - ( player->m_Local.m_flStepSize * 2.0f ) );
+
+ trace_t trace;
+ TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+
+ // Anything between 0.5 and 1.0 is a valid stand value
+ if ( fabs( trace.fraction - 0.5 ) < 0.0004f )
+ {
+ return;
+ }
+
+// if ( trace.fraction == 0.5 )
+// {
+// VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
+// return;
+// }
+
+ if ( trace.fraction > 0.5f )
+ {
+ trace.fraction -= 0.5f;
+ Vector vecNewOrigin;
+ vecNewOrigin = mv->m_vecAbsOrigin + trace.fraction * ( vecStandPos - mv->m_vecAbsOrigin );
+ mv->m_vecAbsOrigin = vecNewOrigin;
+ return;
+ }
+
+ // Less than 0.5 mean we need to attempt to push up the difference.
+ vecStandPos.z = ( mv->m_vecAbsOrigin.z + ( ( 0.5f - trace.fraction ) * ( player->m_Local.m_flStepSize * 2.0f ) ) );
+ TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+
+ // A fraction of 1.0 means we stood up fine - done.
+ if ( trace.fraction == 1.0f )
+ {
+ VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
+ return;
+ }
+
+ // Use the movement stack to pop back and resolve the stand.
+ if ( m_nMovementStackSize > 0 )
+ {
+ VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecPosition, mv->m_vecAbsOrigin );
+ VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecVelocity, mv->m_vecVelocity );
+ m_nMovementStackSize--;
+ ResolveStanding();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::WalkMove2( void )
+{
+ VPROF( "CTFGameMovement::WalkMove2" );
+
+ // Initialize the movement stack.
+ VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[0].m_vecPosition );
+ VectorCopy( m_vecGroundNormal, m_aMovementStack[0].m_vecImpactNormal );
+ m_nMovementStackSize = 1;
+
+ // Calculate the wish velocity and position. The function returns false if
+ // the velocity is zero, it returns true otherwise.
+ Vector vWishPos, vWishDir;
+ float flWishSpeed;
+ if ( !CalcWishVelocityAndPosition( vWishPos, vWishDir, flWishSpeed ) )
+ return;
+
+ // For physics player shadow.
+ mv->m_outWishVel += vWishDir * flWishSpeed;
+
+ // Lift up the players feet (bring the minimum z componenet up by the step
+ // size) and sweep.
+ TryPlayerMove2();
+
+ // Try to stand up at movement's end.
+ ResolveStanding();
+
+ // For physics player shadow.
+ float flStepHeight = mv->m_vecAbsOrigin.z - m_aMovementStack[0].m_vecPosition.z;
+ if ( flStepHeight > 0.0f )
+ {
+ mv->m_outStepHeight += flStepHeight;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::SetMomentumList( float flValue )
+{
+ // Get the TF movement data.
+ CTFMoveData *pTFMove = TFMove();
+ if ( !pTFMove )
+ return;
+
+ // Fill in the momentum list.
+ pTFMove->m_iMomentumHead = 0;
+ for ( int iMomentum = 0; iMomentum < CTFMoveData::MOMENTUM_MAXSIZE; iMomentum++ )
+ {
+ pTFMove->m_aMomentum[iMomentum] = flValue;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovement::AddToMomentumList( float flValue )
+{
+ // Get the TF movement data.
+ CTFMoveData *pTFMove = TFMove();
+ if ( !pTFMove )
+ return;
+
+ // Insert the new gravity value into the list.
+ pTFMove->m_aMomentum[pTFMove->m_iMomentumHead] = flValue;
+ pTFMove->m_iMomentumHead = ( pTFMove->m_iMomentumHead + 1 ) % CTFMoveData::MOMENTUM_MAXSIZE;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFGameMovement::GetMomentum( void )
+{
+ // Get the TF movement data.
+ CTFMoveData *pTFMove = TFMove();
+ if ( !pTFMove )
+ return 1.0f;
+
+ float flTotal = 0.0f;
+ for ( int iMomentum = 0; iMomentum < CTFMoveData::MOMENTUM_MAXSIZE; iMomentum++ )
+ {
+ flTotal += pTFMove->m_aMomentum[iMomentum];
+ }
+
+ flTotal /= CTFMoveData::MOMENTUM_MAXSIZE;
+
+ return flTotal;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovement::CheckJumpButton( void )
+{
+ if (player->pl.deadflag)
+ {
+ mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released
+ return false;
+ }
+
+ // See if we are waterjumping. If so, decrement count and return.
+ if (player->m_flWaterJumpTime)
+ {
+ player->m_flWaterJumpTime -= gpGlobals->frametime;
+ if (player->m_flWaterJumpTime < 0)
+ player->m_flWaterJumpTime = 0;
+
+ return false;
+ }
+
+ // If we are in the water most of the way...
+ if ( player->GetWaterLevel() >= 2 )
+ {
+ // swimming, not jumping
+ SetGroundEntity( NULL );
+
+ if(player->GetWaterType() == CONTENTS_WATER) // We move up a certain amount
+ mv->m_vecVelocity[2] = 100;
+ else if (player->GetWaterType() == CONTENTS_SLIME)
+ mv->m_vecVelocity[2] = 80;
+
+ // play swiming sound
+ if ( player->m_flSwimSoundTime <= 0 )
+ {
+ // Don't play sound again for 1 second
+ player->m_flSwimSoundTime = 1000;
+ PlaySwimSound();
+ }
+
+ return false;
+ }
+
+ // No more effect
+ if (player->GetGroundEntity() == NULL)
+ {
+ mv->m_nOldButtons |= IN_JUMP;
+ return false; // in air, so no effect
+ }
+
+ if ( mv->m_nOldButtons & IN_JUMP )
+ return false; // don't pogo stick
+
+ // In the air now.
+ SetGroundEntity( NULL );
+
+ PlayStepSound( m_pSurfaceData, 1.0, true );
+
+ MoveHelper()->PlayerSetAnimation( PLAYER_JUMP );
+
+ float flGroundFactor = 1.0f;
+ if (m_pSurfaceData)
+ {
+ flGroundFactor = m_pSurfaceData->game.jumpFactor;
+ }
+
+ float flMul;
+ flMul = sqrt(2 * GetCurrentGravity() * 45.0);
+
+ // Acclerate upward
+ // If we are ducking...
+ float startz = mv->m_vecVelocity[2];
+ if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
+ {
+ // d = 0.5 * g * t^2 - distance traveled with linear accel
+ // t = sqrt(2.0 * 45 / g) - how long to fall 45 units
+ // v = g * t - velocity at the end (just invert it to jump up that high)
+ // v = g * sqrt(2.0 * 45 / g )
+ // v^2 = g * g * 2.0 * 45 / g
+ // v = sqrt( g * 2.0 * 45 )
+ mv->m_vecVelocity[2] = flGroundFactor * flMul; // 2 * gravity * height
+ }
+ else
+ {
+ mv->m_vecVelocity[2] += flGroundFactor * flMul; // 2 * gravity * height
+ }
+
+ FinishGravity();
+
+ mv->m_outJumpVel.z += mv->m_vecVelocity[2] - startz;
+ mv->m_outStepHeight += 0.1f;
+
+ // Flag that we jumped.
+ mv->m_nOldButtons |= IN_JUMP; // don't jump again until released
+ return true;
+}
diff --git a/game/shared/tf2/tf_gamemovement.h b/game/shared/tf2/tf_gamemovement.h
new file mode 100644
index 0000000..cb72f09
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement.h
@@ -0,0 +1,126 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_H
+#define TF_GAMEMOVEMENT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gamemovement.h"
+#include "tf_movedata.h"
+#include "movevars_shared.h"
+
+class CBaseTFPlayer;
+class CBasePlayer;
+
+//-----------------------------------------------------------------------------
+// This class is the GameMovement class for team fortress and overrides
+// some of the default behavior.
+//-----------------------------------------------------------------------------
+class CTFGameMovement : public CGameMovement
+{
+ // Team Fortress 2 game movement base class.
+ DECLARE_CLASS( CTFGameMovement, CGameMovement );
+
+public:
+
+ // CGameMovement public overrides.
+ virtual void PlayerMove( void );
+
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData ) {}
+
+ // Utility
+ inline CTFMoveData *TFMove( void ) { return static_cast<CTFMoveData*>( mv ); }
+
+protected:
+ // Player movement functions.
+ virtual bool PrePlayerMove( void );
+ virtual void HandlePlayerMove( void );
+ virtual void PostPlayerMove( void );
+
+ // Player pre-movement functions.
+ virtual int CheckStuck( void );
+ virtual void UpdateTimers( void );
+ bool CheckDeath( void );
+ virtual void SetupViewAngles( void );
+ virtual void HandleDuck( void );
+ virtual void FinishUnDuck( void );
+ virtual void SetupSpeed( void );
+ void SpeedCrop( void );
+ void Accelerate( Vector& wishdir, float wishspeed, float accel);
+ void AccelerateWithoutMomentum( Vector& wishdir, float wishspeed, float accel);
+ virtual float GetAirSpeedCap( void );
+ float CalcGravityAdjustment( const Vector &wishdir );
+ void HandleLadder( void );
+ virtual void CategorizePosition( void );
+ virtual bool CheckJumpButton( void );
+
+ virtual void PlayStepSound( surfacedata_t *psurface, float fvol, bool force );
+ // Should the step sound play?
+ virtual bool ShouldPlayStepSound( surfacedata_t *psurface, float fvol );
+
+ // Specific movement functions.
+ virtual void FullWalkMove();
+ virtual void WalkMove( void );
+ virtual void _WalkMove( void );
+ void WalkMove2( void );
+ void AirMove( void );
+ virtual int TryPlayerMove( Vector *pFirstDest=NULL, trace_t *pFirstTrace=NULL );
+ int TryPlayerMove2( void );
+ void ResolveStanding( void );
+ void TryStanding( void );
+ bool ChargeMove( void );
+ bool StunMove( void );
+
+ void EndCharge( void );
+
+ virtual void HandleDuckingSpeedCrop( void );
+
+ // Figures out how the constraint should slow us down
+ float ComputeConstraintSpeedFactor( void );
+
+ // Movement helpers.
+ virtual bool CalcWishVelocityAndPosition( Vector &vWishPos, Vector &vWishDir, float &flWishSpeed );
+ inline void TracePlayerBBoxWithStep( const Vector &vStart, const Vector &vEnd, unsigned int fMask, int collisionGroup, trace_t &trace );
+
+ // Momentum
+ void SetMomentumList( float flValue = 1.0f );
+ void AddToMomentumList( float flValue );
+ float GetMomentum( void );
+
+ // Collision response functions.
+ bool CollisionResponseGeneric( const trace_t &trace, int &nBlocked );
+ void CollisionResponseStuck( void );
+ void CollisionResponseNone( const trace_t &trace );
+ bool RedirectGroundVelocity( const trace_t &trace );
+ bool RedirectAirVelocity( const trace_t &trace );
+ inline int BlockerType( const Vector &vImpactNormal );
+
+protected:
+
+ // Per movement collision data cache(s)
+ Vector m_vecGroundNormal;
+ Vector m_vecOriginalVelocity;
+ int m_nLanding;
+
+ enum { MAX_IMPACT_PLANES = 5 };
+ int m_nImpactPlaneCount;
+ Vector m_aImpactPlaneNormals[MAX_IMPACT_PLANES];
+
+ enum { MOVEMENTSTACK_MAXSIZE = 10 };
+ struct MovementStackData_t
+ {
+ Vector m_vecPosition;
+ Vector m_vecVelocity;
+ Vector m_vecImpactNormal;
+ };
+ int m_nMovementStackSize;
+ MovementStackData_t m_aMovementStack[MOVEMENTSTACK_MAXSIZE];
+};
+
+#endif // TF_GAMEMOVEMENT_H
diff --git a/game/shared/tf2/tf_gamemovement_chooser.cpp b/game/shared/tf2/tf_gamemovement_chooser.cpp
new file mode 100644
index 0000000..d059dae
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_chooser.cpp
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_chooser.h"
+#include "tf_movedata.h"
+
+static CTFGameMovementChooser g_GameMovement;
+IGameMovement *g_pGameMovement = ( IGameMovement* )&g_GameMovement;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementChooser::CTFGameMovementChooser()
+{
+ // Allocate memory for a movement type for each class (0 = undecided)
+ m_Movements.SetSize( TFCLASS_CLASS_COUNT );
+
+ // NOTE: the order here matches the enum order in tf_shareddefs.h
+ m_Movements[TFCLASS_RECON] = &m_ReconMovement;
+ m_Movements[TFCLASS_COMMANDO] = &m_CommandoMovement;
+ m_Movements[TFCLASS_MEDIC] = &m_MedicMovement;
+ m_Movements[TFCLASS_DEFENDER] = &m_DefenderMovement;
+ m_Movements[TFCLASS_SNIPER] = &m_SniperMovement;
+ m_Movements[TFCLASS_SUPPORT] = &m_SupportMovement;
+ m_Movements[TFCLASS_ESCORT] = &m_EscortMovement;
+ m_Movements[TFCLASS_SAPPER] = &m_SapperMovement;
+ m_Movements[TFCLASS_INFILTRATOR] = &m_InfiltratorMovement;
+ m_Movements[TFCLASS_PYRO] = &m_PyroMovement;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementChooser::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData )
+{
+ // Convert CMoveData to CTFMoveData
+ CTFMoveData *pTFMoveData = static_cast<CTFMoveData*>( pMoveData );
+
+ // Cache the current class id
+ m_nClassID = pTFMoveData->m_nClassID;
+
+ // Player class movement. (If possible)
+ if ( m_nClassID != TFCLASS_UNDECIDED )
+ {
+ m_Movements[m_nClassID]->ProcessClassMovement( (CBaseTFPlayer *)pPlayer, pTFMoveData );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementChooser::GetPlayerMins( bool ducked ) const
+{
+ // Player class mins.
+ return m_Movements[m_nClassID]->GetPlayerMins( ducked );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementChooser::GetPlayerMaxs( bool ducked ) const
+{
+ return m_Movements[m_nClassID]->GetPlayerMins( ducked );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementChooser::GetPlayerViewOffset( bool ducked ) const
+{
+ return m_Movements[m_nClassID]->GetPlayerViewOffset( ducked );
+}
diff --git a/game/shared/tf2/tf_gamemovement_chooser.h b/game/shared/tf2/tf_gamemovement_chooser.h
new file mode 100644
index 0000000..a1284bc
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_chooser.h
@@ -0,0 +1,69 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_CHOOSER_H
+#define TF_GAMEMOVEMENT_CHOOSER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlvector.h"
+#include "IGameMovement.h"
+#include "tf_gamemovement.h"
+#include "tf_gamemovement_recon.h"
+#include "tf_gamemovement_commando.h"
+#include "tf_gamemovement_medic.h"
+#include "tf_gamemovement_defender.h"
+#include "tf_gamemovement_sniper.h"
+#include "tf_gamemovement_support.h"
+#include "tf_gamemovement_escort.h"
+#include "tf_gamemovement_sapper.h"
+#include "tf_gamemovement_infiltrator.h"
+#include "tf_gamemovement_pyro.h"
+
+//=============================================================================
+//
+// Team Fortess Game Movement Chooser
+//
+class CTFGameMovementChooser : public IGameMovement
+{
+public:
+
+ CTFGameMovementChooser();
+
+ // Process the current movement command
+ virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMoveData );
+
+ // Allows other parts of the engine to find out the normal and ducked player bbox sizes
+ virtual const Vector &GetPlayerMins( bool ducked ) const;
+ virtual const Vector &GetPlayerMaxs( bool ducked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool ducked ) const;
+
+protected:
+
+ // Cache the current class id.
+ int m_nClassID;
+
+ // Create the class specific movement singletons.
+ CTFGameMovementRecon m_ReconMovement;
+ CTFGameMovementCommando m_CommandoMovement;
+ CTFGameMovementMedic m_MedicMovement;
+ CTFGameMovementDefender m_DefenderMovement;
+ CTFGameMovementSniper m_SniperMovement;
+ CTFGameMovementSupport m_SupportMovement;
+ CTFGameMovementEscort m_EscortMovement;
+ CTFGameMovementSapper m_SapperMovement;
+ CTFGameMovementInfiltrator m_InfiltratorMovement;
+ CTFGameMovementPyro m_PyroMovement;
+
+ // Vector of class specific movements (for quick addressing).
+ CUtlVector<CTFGameMovement*> m_Movements;
+};
+
+extern IGameMovement *g_pGameMovement;
+
+#endif // TF_GAMEMOVEMENT_CHOOSER_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_commando.cpp b/game/shared/tf2/tf_gamemovement_commando.cpp
new file mode 100644
index 0000000..bcdfce3
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_commando.cpp
@@ -0,0 +1,424 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "in_buttons.h"
+#include "tf_gamemovement_commando.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementCommando::CTFGameMovementCommando()
+{
+ m_pCommandoData = NULL;
+
+ m_vStandMins = COMMANDOCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = COMMANDOCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = COMMANDOCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = COMMANDOCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = COMMANDOCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = COMMANDOCLASS_VIEWOFFSET_DUCK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementCommando::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassCommandoData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+
+ // Is this how I want to handle this???
+// m_pCommandoData = &pTFMoveData->CommandoData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementCommando::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementCommando::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementCommando::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovementCommando::CheckDoubleTapForward( void )
+{
+ // Check for other movement keys!!!
+ if ( ( TFMove()->m_nButtons & IN_MOVELEFT ) || ( TFMove()->m_nButtons & IN_MOVERIGHT ) ||
+ ( TFMove()->m_nButtons & IN_BACK ) || ( TFMove()->m_nButtons & IN_JUMP ) )
+ {
+ TFMove()->CommandoData().m_flDoubleTapForwardTime = COMMANDO_TIME_INVALID;
+ return false;
+ }
+
+
+ if ( ( TFMove()->m_nButtons & IN_FORWARD ) && !( TFMove()->m_nOldButtons & IN_FORWARD ) )
+ {
+ // Start timer.
+ if ( TFMove()->CommandoData().m_flDoubleTapForwardTime == COMMANDO_TIME_INVALID )
+ {
+ TFMove()->CommandoData().m_flDoubleTapForwardTime = COMMANDO_DOUBLETAP_TIME;
+ }
+ // Check for a double tap.
+ else
+ {
+ if ( TFMove()->CommandoData().m_flDoubleTapForwardTime > 0.0f )
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementCommando::CheckBullRush( void )
+{
+ // Don't check for bullrush if we are dead!
+ if ( IsDead() )
+ return;
+
+ // Don't go into bullrush if noclipping
+ if ( player->GetMoveType() == MOVETYPE_NOCLIP )
+ return;
+
+ // Cannot bullrush inside of a vehicle (manned <gun>).
+#if !defined (CLIENT_DLL)
+ IServerVehicle *pVehicle = player->GetVehicle();
+ if ( pVehicle )
+ return;
+#else
+ IClientVehicle *pVehicle = player->GetVehicle();
+ if ( pVehicle )
+ return;
+#endif
+
+ if ( CheckDoubleTapForward() && !TFMove()->CommandoData().m_bBullRush &&
+ TFMove()->CommandoData().m_bCanBullRush && !( player->GetFlags() & FL_DUCKING ) )
+ {
+ // Set in a bull rush.
+ TFMove()->CommandoData().m_bBullRush = true;
+
+ // Set timers.
+ TFMove()->CommandoData().m_flBullRushTime = COMMANDO_BULLRUSH_TIME;
+
+ // Lock view/move angles
+ Vector vBullrushDir;
+ AngleVectors( TFMove()->m_vecViewAngles, &vBullrushDir, NULL, NULL );
+ TFMove()->CommandoData().m_vecBullRushDir = vBullrushDir;
+ TFMove()->CommandoData().m_vecBullRushViewDir = TFMove()->m_vecViewAngles;
+ TFMove()->CommandoData().m_vecBullRushViewGoalDir.Init();
+ TFMove()->CommandoData().m_vecBullRushViewGoalDir.SetY( TFMove()->m_vecViewAngles.y );
+
+ // Set movement type.
+ player->SetMoveType( (MoveType_t)COMMANDO_MOVETYPE_BULLRUSH );
+ player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovementCommando::PrePlayerMove( void )
+{
+ // Assume we don't touch anything (Reset the touch list).
+ MoveHelper()->ResetTouchList();
+
+ // Check to see if we are stuck.
+ if ( CheckStuck() )
+ return false;
+
+ CheckBullRush();
+
+ // Update (reduce) movement timers.
+ UpdateTimers();
+
+ // Check to see if the player is dead and setup death data, otherwise setup
+ // the players view angles.
+ if ( !CheckDeath() )
+ {
+ SetupViewAngles();
+ }
+
+ // Handle ducking.
+ HandleDuck();
+
+ // Handle ladder.
+ HandleLadder();
+
+ // Categorize the player's position.
+ CategorizePosition();
+
+ // Calculate the player's movement speed (has to happen after categorize position)
+ SetupSpeed();
+
+ // Update our stepping sound (based on the player's location).
+ player->UpdateStepSound( m_pSurfaceData, mv->m_vecAbsOrigin, mv->m_vecVelocity );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementCommando::HandlePlayerMove( void )
+{
+ // Handle the specific bull rush movement type.
+ if ( player->GetMoveType() == COMMANDO_MOVETYPE_BULLRUSH )
+ {
+ BullRushMove();
+ return;
+ }
+
+ // Let the default TF2 player movement code handle the move.
+ BaseClass::HandlePlayerMove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementCommando::HandleDuck( void )
+{
+ if ( player->GetMoveType() == COMMANDO_MOVETYPE_BULLRUSH )
+ return;
+
+ BaseClass::HandleDuck();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementCommando::UpdateTimers( void )
+{
+ BaseClass::UpdateTimers();
+
+ CTFMoveData *pMoveData = TFMove();
+ if ( !pMoveData )
+ return;
+
+ float frame_msec = 1000.0f * gpGlobals->frametime;
+
+ // Decrement the bull rush time.
+ if ( pMoveData->CommandoData().m_flBullRushTime != COMMANDO_TIME_INVALID )
+ {
+ if ( pMoveData->CommandoData().m_flBullRushTime > 0.0f )
+ {
+ pMoveData->CommandoData().m_flBullRushTime -= frame_msec;
+ }
+ else
+ {
+ TFMove()->CommandoData().m_bBullRush = false;
+ TFMove()->CommandoData().m_flBullRushTime = COMMANDO_TIME_INVALID;
+ player->SetMoveType( MOVETYPE_WALK );
+ player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
+ }
+ }
+
+ if ( pMoveData->CommandoData().m_flDoubleTapForwardTime != COMMANDO_TIME_INVALID )
+ {
+ if ( pMoveData->CommandoData().m_flDoubleTapForwardTime > 0.0f )
+ {
+ pMoveData->CommandoData().m_flDoubleTapForwardTime -= frame_msec;
+ }
+ else
+ {
+ pMoveData->CommandoData().m_flDoubleTapForwardTime = COMMANDO_TIME_INVALID;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementCommando::SetupViewAngles( void )
+{
+
+ BaseClass::SetupViewAngles();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementCommando::SetupSpeed( void )
+{
+ BaseClass::SetupSpeed();
+
+ if ( player->GetMoveType() == COMMANDO_MOVETYPE_BULLRUSH )
+ {
+ mv->m_flMaxSpeed = sv_maxspeed.GetFloat();
+
+ // Slow down by the speed factor
+ if (m_pSurfaceData)
+ {
+ mv->m_flMaxSpeed *= m_pSurfaceData->game.maxSpeedFactor;
+ }
+
+ mv->m_flForwardMove = TFMove()->m_flClientMaxSpeed * 4.0f;
+ mv->m_flUpMove = 0.0f;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFGameMovementCommando::CalcWishVelocityAndPosition( Vector &vWishPos, Vector &vWishDir, float &flWishSpeed )
+{
+ //
+ // Determine the movement angles.
+ //
+ Vector vForward, vRight, vUp;
+ if ( player->GetMoveType() == COMMANDO_MOVETYPE_BULLRUSH )
+ {
+ vForward = TFMove()->CommandoData().m_vecBullRushDir;
+ // Cross the bullrush direction with the z-axis to get the right vector.
+ CrossProduct( vForward, Vector( 0.0f, 0.0f, 1.0f ), vRight );
+ vUp.Init();
+ }
+ else
+ {
+ AngleVectors( mv->m_vecViewAngles, &vForward, &vRight, &vUp );
+
+ //
+ // Zero out the z component of the movement vectors and renormalize.
+ //
+ vForward.z = 0.0f;
+ VectorNormalize( vForward );
+ vRight.z = 0.0f;
+ VectorNormalize( vRight );
+ }
+
+ //
+ // Determine the xy parts of the velocity.
+ //
+ Vector vWishVel( 0.0f, 0.0f, 0.0f );
+ for ( int axis = 0; axis < 2; axis++ )
+ {
+ vWishVel[axis] = ( vForward[axis] * mv->m_flForwardMove ) +
+ ( vRight[axis] * mv->m_flSideMove );
+ }
+ vWishVel.z = 0.0f;
+
+ //
+ // Componentize the velocity into direction and speed.
+ //
+ VectorCopy( vWishVel, vWishDir );
+ flWishSpeed = VectorNormalize( vWishDir );
+
+ if ( flWishSpeed > mv->m_flMaxSpeed )
+ {
+ VectorScale( vWishVel, ( mv->m_flMaxSpeed / flWishSpeed ), vWishVel );
+ flWishSpeed = mv->m_flMaxSpeed;
+ }
+
+ //
+ // Accelerate (in the plane).
+ //
+ mv->m_vecVelocity.z = 0.0f;
+ Accelerate( vWishDir, flWishSpeed, sv_accelerate.GetFloat() );
+ mv->m_vecVelocity.z = 0.0f;
+
+ // Add in any base velocity (from conveyers, etc.) to the current velocity.
+ VectorAdd( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+
+ //
+ // Stop the player (zero out velocity) if the player's speed is below a
+ // given threshold.
+ //
+ float flSpeed = VectorLength( mv->m_vecVelocity );
+ if ( flSpeed < 1.0f /*SPEED_STOP_THRESHOLD*/ )
+ {
+ mv->m_vecVelocity.Init();
+ return false;
+ }
+
+ //
+ // Calculate the wish position.
+ //
+ vWishPos.x = mv->m_vecAbsOrigin.x + ( mv->m_vecVelocity.x * gpGlobals->frametime );
+ vWishPos.y = mv->m_vecAbsOrigin.y + ( mv->m_vecVelocity.y * gpGlobals->frametime );
+ vWishPos.z = mv->m_vecAbsOrigin.z;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementCommando::BullRushMove( void )
+{
+ CTFMoveData *pMoveData = TFMove();
+ if ( !pMoveData )
+ return;
+
+ // Ignoring water for now!!!!
+ StartGravity();
+
+ // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor,
+ // we don't slow when standing still, relative to the conveyor.
+ if (player->GetGroundEntity() != NULL)
+ {
+ mv->m_vecVelocity[2] = 0.0;
+ Friction();
+ }
+
+ // Make sure velocity is valid.
+ CheckVelocity();
+
+ if (player->GetGroundEntity() != NULL)
+ {
+ WalkMove2();
+ }
+ else
+ {
+ AirMove(); // Take into account movement when in air.
+ }
+
+ // Set final flags.
+ CategorizePosition();
+
+ // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like
+ // a conveyor (or maybe another monster?)
+ VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+
+ // Make sure velocity is valid.
+ CheckVelocity();
+
+ // Add any remaining gravitational component.
+ FinishGravity();
+
+ // If we are on ground, no downward velocity.
+ if ( player->GetGroundEntity() != NULL )
+ {
+ mv->m_vecVelocity[2] = 0;
+ }
+
+ CheckFalling();
+} \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_commando.h b/game/shared/tf2/tf_gamemovement_commando.h
new file mode 100644
index 0000000..fa3982a
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_commando.h
@@ -0,0 +1,64 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_COMMANDO_H
+#define TF_GAMEMOVEMENT_COMMANDO_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Commando Game Movement Class
+//
+class CTFGameMovementCommando : public CTFGameMovement
+{
+public:
+
+ DECLARE_CLASS( CTFGameMovementCommando, CTFGameMovement );
+
+ CTFGameMovementCommando();
+
+ // Interface Implementation
+ void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ Vector const &GetPlayerMins( bool bDucked ) const;
+ Vector const &GetPlayerMaxs( bool bDucked ) const;
+ Vector const &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ // TF2 movement overrides.
+ bool PrePlayerMove( void );
+ void HandlePlayerMove( void );
+ void HandleDuck( void );
+
+ void SetupViewAngles( void );
+ void UpdateTimers( void );
+ void SetupSpeed( void );
+
+ bool CalcWishVelocityAndPosition( Vector &vWishPos, Vector &vWishDir, float &flWishSpeed );
+
+ bool CheckDoubleTapForward( void );
+
+ void CheckBullRush( void );
+ void BullRushMove( void );
+
+ PlayerClassCommandoData_t *m_pCommandoData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_COMMANDO_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_defender.cpp b/game/shared/tf2/tf_gamemovement_defender.cpp
new file mode 100644
index 0000000..e485ff5
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_defender.cpp
@@ -0,0 +1,65 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_defender.h"
+#include "tf_movedata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementDefender::CTFGameMovementDefender()
+{
+ m_pDefenderData = NULL;
+
+ m_vStandMins = DEFENDERCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = DEFENDERCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = DEFENDERCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = DEFENDERCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = DEFENDERCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = DEFENDERCLASS_VIEWOFFSET_DUCK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementDefender::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassDefenderData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+ m_pDefenderData = &pTFMoveData->DefenderData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementDefender::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementDefender::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementDefender::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
diff --git a/game/shared/tf2/tf_gamemovement_defender.h b/game/shared/tf2/tf_gamemovement_defender.h
new file mode 100644
index 0000000..e71feb7
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_defender.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_DEFENDER_H
+#define TF_GAMEMOVEMENT_DEFENDER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Defender Game Movement Class
+//
+class CTFGameMovementDefender : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementDefender, CTFGameMovement );
+
+public:
+
+ CTFGameMovementDefender();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ PlayerClassDefenderData_t *m_pDefenderData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_DEFENDER_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_escort.cpp b/game/shared/tf2/tf_gamemovement_escort.cpp
new file mode 100644
index 0000000..7ce0c48
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_escort.cpp
@@ -0,0 +1,65 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_escort.h"
+#include "tf_movedata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementEscort::CTFGameMovementEscort()
+{
+ m_pEscortData = NULL;
+
+ m_vStandMins = ESCORTCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = ESCORTCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = ESCORTCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = ESCORTCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = ESCORTCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = ESCORTCLASS_VIEWOFFSET_DUCK;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementEscort::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassEscortData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+ m_pEscortData = &pTFMoveData->EscortData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementEscort::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementEscort::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementEscort::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
diff --git a/game/shared/tf2/tf_gamemovement_escort.h b/game/shared/tf2/tf_gamemovement_escort.h
new file mode 100644
index 0000000..c07d9b8
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_escort.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_ESCORT_H
+#define TF_GAMEMOVEMENT_ESCORT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Escort Game Movement Class
+//
+class CTFGameMovementEscort : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementEscort, CTFGameMovement );
+
+public:
+
+ CTFGameMovementEscort();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ PlayerClassEscortData_t *m_pEscortData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_ESCORT_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_infiltrator.cpp b/game/shared/tf2/tf_gamemovement_infiltrator.cpp
new file mode 100644
index 0000000..543f1e2
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_infiltrator.cpp
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_infiltrator.h"
+#include "tf_movedata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementInfiltrator::CTFGameMovementInfiltrator()
+{
+ m_pInfiltratorData = NULL;
+
+ m_vStandMins = INFILTRATORCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = INFILTRATORCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = INFILTRATORCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = INFILTRATORCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = INFILTRATORCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = INFILTRATORCLASS_VIEWOFFSET_DUCK;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementInfiltrator::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassInfiltratorData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+ m_pInfiltratorData = &pTFMoveData->InfiltratorData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementInfiltrator::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementInfiltrator::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementInfiltrator::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
diff --git a/game/shared/tf2/tf_gamemovement_infiltrator.h b/game/shared/tf2/tf_gamemovement_infiltrator.h
new file mode 100644
index 0000000..16b5f59
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_infiltrator.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_INFILTRATOR_H
+#define TF_GAMEMOVEMENT_INFILTRATOR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Infiltrator Game Movement Class
+//
+class CTFGameMovementInfiltrator : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementInfiltrator, CTFGameMovement );
+
+public:
+
+ CTFGameMovementInfiltrator();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ PlayerClassInfiltratorData_t *m_pInfiltratorData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_INFILTRATOR_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_medic.cpp b/game/shared/tf2/tf_gamemovement_medic.cpp
new file mode 100644
index 0000000..1982004
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_medic.cpp
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_medic.h"
+#include "tf_movedata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementMedic::CTFGameMovementMedic()
+{
+ m_pMedicData = NULL;
+
+ m_vStandMins = MEDICCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = MEDICCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = MEDICCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = MEDICCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = MEDICCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = MEDICCLASS_VIEWOFFSET_DUCK;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementMedic::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassMedicData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+ m_pMedicData = &pTFMoveData->MedicData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementMedic::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementMedic::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementMedic::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
diff --git a/game/shared/tf2/tf_gamemovement_medic.h b/game/shared/tf2/tf_gamemovement_medic.h
new file mode 100644
index 0000000..223c741
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_medic.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_MEDIC_H
+#define TF_GAMEMOVEMENT_MEDIC_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Medic Game Movement Class
+//
+class CTFGameMovementMedic : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementMedic, CTFGameMovement );
+
+public:
+
+ CTFGameMovementMedic();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ PlayerClassMedicData_t *m_pMedicData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_MEDIC_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_pyro.cpp b/game/shared/tf2/tf_gamemovement_pyro.cpp
new file mode 100644
index 0000000..8bbc2d8
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_pyro.cpp
@@ -0,0 +1,63 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_pyro.h"
+#include "tf_movedata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementPyro::CTFGameMovementPyro()
+{
+ m_pPyroData = NULL;
+
+ m_vStandMins = PYROCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = PYROCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = PYROCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = PYROCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = PYROCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = PYROCLASS_VIEWOFFSET_DUCK;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementPyro::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassPyroData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+ m_pPyroData = &pTFMoveData->PyroData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementPyro::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementPyro::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementPyro::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
+
diff --git a/game/shared/tf2/tf_gamemovement_pyro.h b/game/shared/tf2/tf_gamemovement_pyro.h
new file mode 100644
index 0000000..8aa25b5
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_pyro.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_PYRO_H
+#define TF_GAMEMOVEMENT_PYRO_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Pyro Game Movement Class
+//
+class CTFGameMovementPyro : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementPyro, CTFGameMovement );
+
+public:
+
+ CTFGameMovementPyro();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ PlayerClassPyroData_t *m_pPyroData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_PYRO_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_recon.cpp b/game/shared/tf2/tf_gamemovement_recon.cpp
new file mode 100644
index 0000000..c9b8987
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_recon.cpp
@@ -0,0 +1,595 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_recon.h"
+#include "tf_movedata.h"
+#include "in_buttons.h"
+
+#define TIME_WALL_SUPPRESSION_JUMP 100
+#define TIME_WALL_SUPPRESSION_IMPACT 400
+#define TIME_WALL_STICK 50
+#define TIME_STRAFE_STICK 50
+#define TIME_LEAP_STICK 300
+#define TIME_WALL_ACTIVATE_JUMP 300
+#define TIME_WALL_INVALID -99999
+#define MAX_VERTICAL_SPEED 400
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementRecon::CTFGameMovementRecon()
+{
+ m_pReconData = NULL;
+
+ m_vStandMins = RECONCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = RECONCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = RECONCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = RECONCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = RECONCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = RECONCLASS_VIEWOFFSET_DUCK;
+
+ m_bPerformingAirMove = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementRecon::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMove )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassReconData_t::PLAYERCLASS_ID == pTFMove->m_nClassID );
+ m_pReconData = &pTFMove->ReconData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, pTFMove );
+
+ // Set the jump count appropriately...
+ // If we've hit the ground, we can double jump again...
+ if ((player->GetGroundEntity() != NULL) && (pTFMove->ReconData().m_flStickTime == TIME_WALL_INVALID))
+ {
+ m_pReconData->m_nJumpCount = 0;
+ ResetWallImpact( (CTFMoveData*)mv );
+ }
+}
+
+void CTFGameMovementRecon::PostPlayerMove( void )
+{
+ BaseClass::PostPlayerMove( );
+
+ if (m_pReconData->m_flStickTime != TIME_WALL_INVALID)
+ {
+ // We're stuck, so stick!
+ mv->m_vecVelocity.Init( 0.0f, 0.0f, 0.0f );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementRecon::UpdateTimers( void )
+{
+ BaseClass::UpdateTimers();
+
+ CTFMoveData *pTFMove = TFMove();
+ if ( !pTFMove )
+ return;
+
+ float frame_msec = 1000.0f * gpGlobals->frametime;
+
+ // Decrement the recon timers.
+ if ( pTFMove->ReconData().m_flSuppressionJumpTime != TIME_WALL_INVALID )
+ {
+ pTFMove->ReconData().m_flSuppressionJumpTime -= frame_msec;
+ if ( pTFMove->ReconData().m_flSuppressionJumpTime <= 0.0f )
+ {
+ pTFMove->ReconData().m_flSuppressionJumpTime = TIME_WALL_INVALID;
+ }
+ }
+
+ if ( pTFMove->ReconData().m_flSuppressionImpactTime != TIME_WALL_INVALID )
+ {
+ pTFMove->ReconData().m_flSuppressionImpactTime -= frame_msec;
+ if ( pTFMove->ReconData().m_flSuppressionImpactTime <= 0.0f )
+ {
+ pTFMove->ReconData().m_flSuppressionImpactTime = TIME_WALL_INVALID;
+ }
+ }
+
+ if ( pTFMove->ReconData().m_flActiveJumpTime != TIME_WALL_INVALID )
+ {
+ pTFMove->ReconData().m_flActiveJumpTime -= frame_msec;
+ if ( pTFMove->ReconData().m_flActiveJumpTime <= 0.0f )
+ {
+ pTFMove->ReconData().m_flActiveJumpTime = TIME_WALL_INVALID;
+ }
+ }
+
+ if ( pTFMove->ReconData().m_flStickTime != TIME_WALL_INVALID )
+ {
+ pTFMove->ReconData().m_flStickTime -= frame_msec;
+ if ( pTFMove->ReconData().m_flStickTime <= 0.0f )
+ {
+ pTFMove->ReconData().m_flStickTime = TIME_WALL_INVALID;
+
+ // Restore velocity at this time
+ pTFMove->m_vecVelocity = pTFMove->ReconData().m_vecUnstickVelocity;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Implement this if you want to know when the player collides during OnPlayerMove
+//-----------------------------------------------------------------------------
+void CTFGameMovementRecon::OnTryPlayerMoveCollision( trace_t &tr )
+{
+ if ( !m_bPerformingAirMove )
+ return;
+
+ // Only keep track of world collisions
+ if ( tr.DidHitWorld() )
+ {
+ CTFMoveData *pTFMove = TFMove();
+ if ( pTFMove )
+ {
+ if ( ( pTFMove->ReconData().m_flSuppressionJumpTime == TIME_WALL_INVALID ) &&
+ ( pTFMove->ReconData().m_flSuppressionImpactTime == TIME_WALL_INVALID ) )
+ {
+ // No walljumps off of mostly horizontal surfaces...
+ if ( fabs( tr.plane.normal.z ) > 0.9f )
+ return;
+
+ // No walljumps off of the same plane as the last one...
+ if ( (pTFMove->ReconData().m_flImpactDist == tr.plane.dist) &&
+ (VectorsAreEqual(pTFMove->ReconData().m_vecImpactNormal, tr.plane.normal, 1e-2) ) )
+ {
+ return;
+ }
+
+ // If you hit a wall, no double jumps for you
+ pTFMove->ReconData().m_nJumpCount = 2;
+
+ // Play an impact sound
+ MoveHelper()->StartSound( pTFMove->m_vecAbsOrigin, "Recon.WallJump" );
+
+ pTFMove->ReconData().m_vecImpactNormal = tr.plane.normal;
+ pTFMove->ReconData().m_flImpactDist = tr.plane.dist;
+
+ pTFMove->ReconData().m_flActiveJumpTime = TIME_WALL_ACTIVATE_JUMP;
+ pTFMove->ReconData().m_flSuppressionImpactTime = TIME_WALL_SUPPRESSION_IMPACT;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementRecon::AirMove()
+{
+ m_bPerformingAirMove = true;
+
+ // When in the air, recon travels ballistically
+ if ( TFMove()->ReconData().m_nJumpCount )
+ {
+ // Add in any base velocity to the current velocity.
+ VectorAdd( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
+
+ TryPlayerMove();
+ }
+ else
+ {
+ // But if we're falling (or coming up off ladders), treat it normally
+ BaseClass::AirMove();
+ }
+
+ m_bPerformingAirMove = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the jump button to make various jumps
+//-----------------------------------------------------------------------------
+bool CTFGameMovementRecon::CheckWaterJump()
+{
+ // See if we are waterjumping. If so, decrement count and return.
+ if (player->m_flWaterJumpTime)
+ {
+ player->m_flWaterJumpTime -= gpGlobals->frametime;
+ if (player->m_flWaterJumpTime < 0)
+ player->m_flWaterJumpTime = 0;
+
+ return true;
+ }
+
+ // If we are in the water most of the way...
+ if ( player->GetWaterLevel() >= 2 )
+ {
+ // swimming, not jumping
+ SetGroundEntity( NULL );
+
+ if(player->GetWaterType() == CONTENTS_WATER) // We move up a certain amount
+ mv->m_vecVelocity[2] = 100;
+ else if (player->GetWaterType() == CONTENTS_SLIME)
+ mv->m_vecVelocity[2] = 80;
+
+ // play swiming sound
+ if ( player->m_flSwimSoundTime <= 0 )
+ {
+ // Don't play sound again for 1 second
+ player->m_flSwimSoundTime = 1000;
+ PlaySwimSound();
+ }
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Resets the impact time
+//-----------------------------------------------------------------------------
+void CTFGameMovementRecon::ResetWallImpact( CTFMoveData *pTFMove )
+{
+ if ( pTFMove->ReconData().m_flActiveJumpTime != TIME_WALL_INVALID )
+ {
+ pTFMove->ReconData().m_flActiveJumpTime = TIME_WALL_INVALID;
+ pTFMove->ReconData().m_flSuppressionImpactTime = TIME_WALL_INVALID;
+ pTFMove->ReconData().m_vecImpactNormal.Init( 9999, 9999, 9999 );
+ pTFMove->ReconData().m_flImpactDist = -9999.0f;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the jump button to make various jumps
+//-----------------------------------------------------------------------------
+bool CTFGameMovementRecon::CheckWallJump( CTFMoveData *pTFMove )
+{
+ if ( player->GetGroundEntity() != NULL )
+ return false;
+
+ if ( pTFMove->ReconData().m_flActiveJumpTime == TIME_WALL_INVALID )
+ return false;
+
+ // Play a jump sound
+ PlayStepSound( m_pSurfaceData, 1.0, true );
+
+ Vector jumpDir;
+ if ( ( pTFMove->m_nButtons & ( IN_MOVELEFT | IN_MOVERIGHT ) ) )
+ {
+ AngleVectors( pTFMove->m_vecViewAngles, NULL, &jumpDir, NULL );
+
+ // Apply strafe jump...
+ jumpDir *= ( pTFMove->m_nButtons & IN_MOVELEFT ) ? -1.0f : 1.0f;
+ jumpDir.z = 0.0f;
+
+ if ( pTFMove->m_nButtons & ( IN_FORWARD | IN_BACK ) )
+ {
+ Vector forward;
+ AngleVectors( pTFMove->m_vecViewAngles, &forward, NULL, NULL );
+ forward *= 0.5f;
+ forward *= ( pTFMove->m_nButtons & IN_BACK ) ? -1.0f : 1.0f;
+ forward.z = 0.0;
+ jumpDir += forward;
+ }
+
+ VectorNormalize( jumpDir );
+ jumpDir *= 400;
+ }
+ else
+ {
+ AngleVectors( pTFMove->m_vecViewAngles, &jumpDir, NULL, NULL );
+ jumpDir *= ( pTFMove->m_nButtons & IN_BACK ) ? -1.0f : 1.0f;
+ jumpDir.z = 0.0;
+
+ VectorNormalize( jumpDir );
+ jumpDir *= 400;
+ }
+
+ pTFMove->ReconData().m_flStickTime = TIME_WALL_STICK;
+ pTFMove->ReconData().m_vecUnstickVelocity.Init( jumpDir.x, jumpDir.y,
+ pTFMove->m_vecVelocity[2] + 1.5 * sqrt(2 * 800 * 45.0) );
+ if (pTFMove->ReconData().m_vecUnstickVelocity.GetZ() > MAX_VERTICAL_SPEED)
+ pTFMove->ReconData().m_vecUnstickVelocity.SetZ( MAX_VERTICAL_SPEED );
+
+ pTFMove->m_vecVelocity.Init( 0, 0, 0 );
+
+ // Don't allow jump into wall
+ float normalComponent = DotProduct( pTFMove->ReconData().m_vecUnstickVelocity, pTFMove->ReconData().m_vecImpactNormal );
+ if ( normalComponent < 0 )
+ {
+ Vector vUnstickVel;
+ VectorMA( pTFMove->ReconData().m_vecUnstickVelocity, -normalComponent,
+ pTFMove->ReconData().m_vecImpactNormal, vUnstickVel );
+ pTFMove->ReconData().m_vecUnstickVelocity = vUnstickVel;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the jump button to make various jumps
+//-----------------------------------------------------------------------------
+bool CTFGameMovementRecon::CheckBackJump( bool bWasInAir )
+{
+ if ((mv->m_nButtons & IN_BACK) == 0)
+ return false;
+
+ Vector jumpDir, right;
+ AngleVectors( mv->m_vecViewAngles, &jumpDir, &right, NULL );
+ jumpDir.z = 0.0f;
+ jumpDir *= -1.0f;
+
+ if (mv->m_nButtons & (IN_MOVELEFT | IN_MOVERIGHT))
+ {
+ // Apply strafe jump...
+ right *= (mv->m_nButtons & IN_MOVELEFT) ? -1.0f : 1.0f;
+ right.z = 0.0f;
+
+ // Make us not jump quite at a 45% angle if both are selected
+ right *= 0.5f;
+ jumpDir += right;
+ }
+
+ VectorNormalize( jumpDir );
+
+ float flGroundFactor = 1.0f;
+ if ((m_pSurfaceData) /*&& (!bWasInAir)*/ )
+ {
+ flGroundFactor = m_pSurfaceData->game.jumpFactor;
+ }
+
+ jumpDir *= 150 * flGroundFactor;
+
+ // Dampen current motion
+ mv->m_vecVelocity[0] *= 0.5f;
+ mv->m_vecVelocity[1] *= 0.5f;
+
+ float flSideFactor = (bWasInAir) ? 2.0f : 1.0f;
+ float flUpFactor = (bWasInAir) ? 0.5f : 1.5f;
+ flSideFactor *= flGroundFactor;
+ flUpFactor *= flGroundFactor;
+
+ mv->m_vecVelocity[0] += flSideFactor * jumpDir.x;
+ mv->m_vecVelocity[1] += flSideFactor * jumpDir.y;
+ mv->m_vecVelocity[2] += flUpFactor * sqrt(2 * 800 * 45.0);
+
+ mv->m_vecVelocity[0] = clamp( mv->m_vecVelocity[0], -200, 200 );
+ mv->m_vecVelocity[1] = clamp( mv->m_vecVelocity[1], -200, 200 );
+ if (mv->m_vecVelocity[2] > MAX_VERTICAL_SPEED)
+ mv->m_vecVelocity[2] = MAX_VERTICAL_SPEED;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the jump button to make various jumps
+//-----------------------------------------------------------------------------
+bool CTFGameMovementRecon::CheckStrafeJump( bool bWasInAir )
+{
+ if ( (mv->m_nButtons & (IN_MOVELEFT | IN_MOVERIGHT)) == 0 )
+ return false;
+
+ if (mv->m_nButtons & IN_FORWARD)
+ return false;
+
+ Vector jumpDir;
+ AngleVectors( mv->m_vecViewAngles, NULL, &jumpDir, NULL );
+
+ // Apply strafe jump...
+ jumpDir *= (mv->m_nButtons & IN_MOVELEFT) ? -1.0f : 1.0f;
+ jumpDir.z = 0.0f;
+ VectorNormalize( jumpDir );
+
+ float flGroundFactor = 1.0f;
+ if ((m_pSurfaceData) /*&& (!bWasInAir)*/ )
+ {
+ flGroundFactor = m_pSurfaceData->game.jumpFactor;
+ }
+
+ jumpDir *= 300 * flGroundFactor;
+
+ // Dampen current motion
+ mv->m_vecVelocity[0] *= 0.5f;
+ mv->m_vecVelocity[1] *= 0.5f;
+ mv->m_vecVelocity[0] += jumpDir.x;
+ mv->m_vecVelocity[1] += jumpDir.y;
+
+ if (!bWasInAir)
+ mv->m_vecVelocity[2] += flGroundFactor * sqrt(2 * 800 * 45.0); // 2 * gravity * height
+ else
+ mv->m_vecVelocity[2] += 0.5f * sqrt(2 * 800 * 45.0); // 2 * gravity * height
+
+ mv->m_vecVelocity[0] = clamp( mv->m_vecVelocity[0], -400, 400 );
+ mv->m_vecVelocity[1] = clamp( mv->m_vecVelocity[1], -400, 400 );
+ if (mv->m_vecVelocity[2] > MAX_VERTICAL_SPEED)
+ mv->m_vecVelocity[2] = MAX_VERTICAL_SPEED;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the jump button to make various jumps
+//-----------------------------------------------------------------------------
+bool CTFGameMovementRecon::CheckForwardJump( bool bWasInAir )
+{
+ // If we are ducking...
+ if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
+ {
+ // d = 0.5 * g * t^2 - distance traveled with linear accel
+ // t = sqrt(2.0 * 45 / g) - how long to fall 45 units
+ // v = g * t - velocity at the end (just invert it to jump up that high)
+ // v = g * sqrt(2.0 * 45 / g )
+ // v^2 = g * g * 2.0 * 45 / g
+ // v = sqrt( g * 2.0 * 45 )
+ mv->m_vecVelocity[2] = sqrt(2 * 800 * 45.0); // 2 * gravity * height
+ }
+ else
+ {
+ Vector forward, right;
+ AngleVectors( mv->m_vecViewAngles, &forward, &right, NULL );
+ forward.z = 0.0;
+
+ if ((mv->m_nButtons & IN_FORWARD) == 0)
+ {
+ forward.x = forward.y = 0.0f;
+ }
+
+ if (mv->m_nButtons & (IN_MOVELEFT | IN_MOVERIGHT))
+ {
+ // Apply strafe jump...
+ right *= (mv->m_nButtons & IN_MOVELEFT) ? -1.0f : 1.0f;
+ right.z = 0.0f;
+
+ // Make us not jump quite at a 45% angle if both are selected
+ right *= 0.5f;
+ forward += right;
+ }
+
+ VectorNormalize( forward );
+
+ // Slow down by the speed factor
+ float flGroundFactor = 1.0f;
+ if ((m_pSurfaceData) /* && (!bWasInAir) */ )
+ {
+ flGroundFactor = m_pSurfaceData->game.jumpFactor;
+ }
+
+ forward *= 400 * flGroundFactor;
+
+ // Dampen current motion
+ mv->m_vecVelocity[0] *= 0.5f;
+ mv->m_vecVelocity[1] *= 0.5f;
+ mv->m_vecVelocity[0] += forward.x;
+ mv->m_vecVelocity[1] += forward.y;
+
+ float flUpFactor = (bWasInAir) ? 0.7f : 1.0f;
+ flUpFactor *= flGroundFactor;
+
+ mv->m_vecVelocity[2] += flUpFactor * MAX_VERTICAL_SPEED;
+
+ // Limit their velocity in X and Y. We don't want to just clamp because that will change the
+ // direction we're moving in.
+ for ( int i=0; i < 2; i++ )
+ {
+ float flAbs = fabs( mv->m_vecVelocity[i] );
+ if ( flAbs > 400 )
+ {
+ mv->m_vecVelocity[0] *= (400.0f / flAbs);
+ mv->m_vecVelocity[1] *= (400.0f / flAbs);
+ }
+ }
+
+ if (mv->m_vecVelocity[2] > MAX_VERTICAL_SPEED)
+ mv->m_vecVelocity[2] = MAX_VERTICAL_SPEED;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Check the jump button to make various jumps
+//-----------------------------------------------------------------------------
+bool CTFGameMovementRecon::CheckJumpButton()
+{
+ // FIXME: Refactor this so we don't have this complicated duplicate
+ // code here + in gamemovement.cpp
+
+ if ( player->pl.deadflag )
+ {
+ mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released
+ return false;
+ }
+
+ // Water jumps!
+ if ( CheckWaterJump() )
+ return false;
+
+ if ( mv->m_nOldButtons & IN_JUMP )
+ return false; // don't pogo stick
+
+ CTFMoveData *pTFMove = static_cast<CTFMoveData*>( mv );
+
+ // Check for wall jump...
+ if ( !CheckWallJump( pTFMove ) )
+ {
+ // If we already did one air jump, can't do another
+ if ( (player->GetGroundEntity() == NULL ) && ( pTFMove->ReconData().m_nJumpCount > 1) )
+ {
+ mv->m_nOldButtons |= IN_JUMP;
+ return false; // in air, so no effect
+ }
+
+ pTFMove->ReconData().m_nJumpCount += 1;
+
+ // Am I doing a double-jump?
+ bool bWasInAir = (player->GetGroundEntity() == NULL);
+
+ // In the air now.
+ SetGroundEntity( NULL );
+
+ PlayStepSound( m_pSurfaceData, 1.0, true );
+
+ if (!CheckBackJump(bWasInAir))
+ {
+ if (CheckStrafeJump(bWasInAir))
+ {
+ // Can't double jump out of a roll....
+ pTFMove->ReconData().m_nJumpCount += 1;
+ }
+ else
+ {
+ CheckForwardJump(bWasInAir);
+ }
+ }
+ }
+
+ pTFMove->ReconData().m_flSuppressionJumpTime = TIME_WALL_SUPPRESSION_JUMP;
+
+ FinishGravity();
+
+ mv->m_outWishVel = mv->m_vecVelocity;
+ mv->m_outStepHeight += 0.1f;
+
+ // Flag that we jumped.
+ mv->m_nOldButtons |= IN_JUMP; // don't jump again until released
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementRecon::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementRecon::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementRecon::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
diff --git a/game/shared/tf2/tf_gamemovement_recon.h b/game/shared/tf2/tf_gamemovement_recon.h
new file mode 100644
index 0000000..f6cb953
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_recon.h
@@ -0,0 +1,73 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_RECON_H
+#define TF_GAMEMOVEMENT_RECON_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Recon Game Movement Class
+//
+class CTFGameMovementRecon : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementRecon, CTFGameMovement );
+
+public:
+
+ CTFGameMovementRecon();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+ // Purpose:
+ virtual void AirMove();
+
+protected:
+ virtual void PostPlayerMove( void );
+
+ void UpdateTimers( void );
+
+ // Purpose: Check the jump button to make various jumps
+ bool CheckJumpButton();
+
+ // Check various jump types
+ bool CheckWaterJump();
+ bool CheckWallJump(CTFMoveData *pTFMove);
+ bool CheckBackJump( bool bWasInAir );
+ bool CheckStrafeJump( bool bWasInAir );
+ bool CheckForwardJump( bool bWasInAir );
+
+ // Resets the impact time
+ void ResetWallImpact(CTFMoveData *pTFMove);
+
+ // Implement this if you want to know when the player collides during OnPlayerMove
+ virtual void OnTryPlayerMoveCollision( trace_t &tr );
+
+ PlayerClassReconData_t *m_pReconData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+ bool m_bPerformingAirMove;
+};
+
+#endif // TF_GAMEMOVEMENT_RECON_H
diff --git a/game/shared/tf2/tf_gamemovement_sapper.cpp b/game/shared/tf2/tf_gamemovement_sapper.cpp
new file mode 100644
index 0000000..3e10a31
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_sapper.cpp
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_sapper.h"
+#include "tf_movedata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementSapper::CTFGameMovementSapper()
+{
+ m_pSapperData = NULL;
+
+ m_vStandMins = SAPPERCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = SAPPERCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = SAPPERCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = SAPPERCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = SAPPERCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = SAPPERCLASS_VIEWOFFSET_DUCK;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementSapper::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassSapperData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+ m_pSapperData = &pTFMoveData->SapperData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSapper::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSapper::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSapper::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
diff --git a/game/shared/tf2/tf_gamemovement_sapper.h b/game/shared/tf2/tf_gamemovement_sapper.h
new file mode 100644
index 0000000..ecb9c23
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_sapper.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Sapper's game movement
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_SAPPER_H
+#define TF_GAMEMOVEMENT_SAPPER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Sapper Game Movement Class
+//
+class CTFGameMovementSapper : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementSapper, CTFGameMovement );
+
+public:
+
+ CTFGameMovementSapper();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ PlayerClassSapperData_t *m_pSapperData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_SAPPER_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_sniper.cpp b/game/shared/tf2/tf_gamemovement_sniper.cpp
new file mode 100644
index 0000000..3a37ad2
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_sniper.cpp
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_sniper.h"
+#include "tf_movedata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementSniper::CTFGameMovementSniper()
+{
+ m_pSniperData = NULL;
+
+ m_vStandMins = SNIPERCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = SNIPERCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = SNIPERCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = SNIPERCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = SNIPERCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = SNIPERCLASS_VIEWOFFSET_DUCK;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementSniper::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassSniperData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+ m_pSniperData = &pTFMoveData->SniperData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSniper::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSniper::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSniper::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
diff --git a/game/shared/tf2/tf_gamemovement_sniper.h b/game/shared/tf2/tf_gamemovement_sniper.h
new file mode 100644
index 0000000..bad34ca
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_sniper.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_SNIPER_H
+#define TF_GAMEMOVEMENT_SNIPER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Sniper Game Movement Class
+//
+class CTFGameMovementSniper : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementSniper, CTFGameMovement );
+
+public:
+
+ CTFGameMovementSniper();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ PlayerClassSniperData_t *m_pSniperData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_SNIPER_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamemovement_support.cpp b/game/shared/tf2/tf_gamemovement_support.cpp
new file mode 100644
index 0000000..cc6ae77
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_support.cpp
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_gamemovement_support.h"
+#include "tf_movedata.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFGameMovementSupport::CTFGameMovementSupport()
+{
+ m_pSupportData = NULL;
+
+ m_vStandMins = SUPPORTCLASS_HULL_STAND_MIN;
+ m_vStandMaxs = SUPPORTCLASS_HULL_STAND_MAX;
+ m_vStandViewOffset = SUPPORTCLASS_VIEWOFFSET_STAND;
+
+ m_vDuckMins = SUPPORTCLASS_HULL_DUCK_MIN;
+ m_vDuckMaxs = SUPPORTCLASS_HULL_DUCK_MAX;
+ m_vDuckViewOffset = SUPPORTCLASS_VIEWOFFSET_DUCK;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFGameMovementSupport::ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData )
+{
+ // Get the class specific data from the TFMoveData structure
+ Assert( PlayerClassSupportData_t::PLAYERCLASS_ID == pTFMoveData->m_nClassID );
+ m_pSupportData = &pTFMoveData->SupportData();
+
+ // to test pass it through!!
+ BaseClass::ProcessMovement( (CBasePlayer *)pPlayer, static_cast<CMoveData*>( pTFMoveData ) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSupport::GetPlayerMins( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMins : m_vStandMins;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSupport::GetPlayerMaxs( bool bDucked ) const
+{
+ return bDucked ? m_vDuckMaxs : m_vStandMaxs;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const Vector &CTFGameMovementSupport::GetPlayerViewOffset( bool bDucked ) const
+{
+ return bDucked ? m_vDuckViewOffset : m_vStandViewOffset;
+}
diff --git a/game/shared/tf2/tf_gamemovement_support.h b/game/shared/tf2/tf_gamemovement_support.h
new file mode 100644
index 0000000..9523174
--- /dev/null
+++ b/game/shared/tf2/tf_gamemovement_support.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Auto Repair
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMEMOVEMENT_SUPPORT_H
+#define TF_GAMEMOVEMENT_SUPPORT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_gamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CTFMoveData;
+
+//=============================================================================
+//
+// Support Game Movement Class
+//
+class CTFGameMovementSupport : public CTFGameMovement
+{
+
+ DECLARE_CLASS( CTFGameMovementSupport, CTFGameMovement );
+
+public:
+
+ CTFGameMovementSupport();
+
+ // Interface Implementation
+// virtual void ProcessMovement( CTFMoveData *pTFMoveData );
+ virtual void ProcessClassMovement( CBaseTFPlayer *pPlayer, CTFMoveData *pTFMoveData );
+ virtual const Vector &GetPlayerMins( bool bDucked ) const;
+ virtual const Vector &GetPlayerMaxs( bool bDucked ) const;
+ virtual const Vector &GetPlayerViewOffset( bool bDucked ) const;
+
+protected:
+
+ PlayerClassSupportData_t *m_pSupportData;
+ Vector m_vStandMins;
+ Vector m_vStandMaxs;
+ Vector m_vStandViewOffset;
+ Vector m_vDuckMins;
+ Vector m_vDuckMaxs;
+ Vector m_vDuckViewOffset;
+};
+
+#endif // TF_GAMEMOVEMENT_SUPPORT_H \ No newline at end of file
diff --git a/game/shared/tf2/tf_gamerules.cpp b/game/shared/tf2/tf_gamerules.cpp
new file mode 100644
index 0000000..cf9b980
--- /dev/null
+++ b/game/shared/tf2/tf_gamerules.cpp
@@ -0,0 +1,2242 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The TF Game rules
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "tf_gamerules.h"
+#include "tf_shareddefs.h"
+#include "ammodef.h"
+#include "basetfcombatweapon_shared.h"
+#include <KeyValues.h>
+
+
+#ifdef CLIENT_DLL
+
+ #include "c_shield.h"
+ #include "c_te_effect_dispatch.h"
+ #define CShield C_Shield
+
+#else
+
+ #include "tf_shield.h"
+ #include "te_effect_dispatch.h"
+ #include "player.h"
+ #include "tf_player.h"
+ #include "game.h"
+ #include "gamerules.h"
+ #include "teamplay_gamerules.h"
+ #include "menu_base.h"
+ #include "ammodef.h"
+ #include "techtree.h"
+ #include "tf_team.h"
+ #include "tf_shield.h"
+ #include "mathlib/mathlib.h"
+ #include "entitylist.h"
+ #include "basecombatweapon.h"
+ #include "voice_gamemgr.h"
+ #include "tf_class_infiltrator.h"
+ #include "team_messages.h"
+ #include "ndebugoverlay.h"
+ #include "bot_base.h"
+ #include "vstdlib/random.h"
+ #include "info_act.h"
+ #include "igamesystem.h"
+ #include "filesystem.h"
+ #include "info_vehicle_bay.h"
+ #include "IserverVehicle.h"
+ #include "weapon_builder.h"
+ #include "weapon_objectselection.h"
+ #include "tf_player_resource.h"
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+REGISTER_GAMERULES_CLASS( CTeamFortress );
+
+
+#ifdef CLIENT_DLL
+
+
+#else
+
+ #define MAX_OBJECT_COMMAND_DISTANCE 120.0f
+
+ class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
+ {
+ public:
+ virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker )
+ {
+ // Gagged players can't talk at all
+ if ( ((CBaseTFPlayer*)pTalker)->CanSpeak() == false )
+ return false;
+
+ // Dead players can only be heard by other dead team mates
+ if ( pTalker->IsAlive() == false )
+ {
+ if ( pListener->IsAlive() == false )
+ return ( pListener->InSameTeam( pTalker ) );
+
+ return false;
+ }
+
+ return ( pListener->InSameTeam( pTalker ) );
+ }
+ };
+ CVoiceGameMgrHelper g_VoiceGameMgrHelper;
+ IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
+
+
+ // Load the objects.txt file.
+ class CObjectsFileLoad : public CAutoGameSystem
+ {
+ public:
+ virtual bool Init()
+ {
+ LoadObjectInfos( filesystem );
+ return true;
+ }
+ } g_ObjectsFileLoad;
+
+
+
+ extern bool g_fGameOver;
+ float g_flNextReinforcementTime = 0.0f;
+ extern ConVar tf_knockdowntime;
+
+ // Time between reinforcements
+ #define REINFORCEMENT_TIME 15.0
+
+ ConVar sk_plr_dmg_grenade ( "sk_plr_dmg_grenade","0");
+
+ char *sTeamNames[] =
+ {
+ "Unassigned",
+ "Spectator",
+ "Human",
+ "Alien",
+ };
+
+ // Handle the "PossessBot" command.
+ void PossessBot_f()
+ {
+ CBaseTFPlayer *pPlayer = CBaseTFPlayer::Instance( UTIL_GetCommandClientIndex() );
+ if ( !pPlayer )
+ return;
+
+ // Put the local player in control of this bot.
+ if ( args.ArgC() != 2 )
+ {
+ Warning( "PossessBot <client index>\n" );
+ return;
+ }
+
+ int iBotClient = atoi( args[1) );
+ int iBotEnt = iBotClient + 1;
+
+ if ( iBotClient < 0 ||
+ iBotClient >= gpGlobals->maxClients ||
+ pPlayer->entindex() == iBotEnt )
+ {
+ Warning( "PossessBot <client index>\n" );
+ }
+ else
+ {
+ edict_t *pPlayerData = pPlayer->edict();
+ edict_t *pBotData = engine->PEntityOfEntIndex( iBotEnt );
+ if ( pBotData && pBotData->GetUnknown() )
+ {
+ // SWAP EDICTS
+
+ // Backup things we don't want to swap.
+ edict_t oldPlayerData = *pPlayerData;
+ edict_t oldBotData = *pBotData;
+
+ // Swap edicts.
+ edict_t tmp = *pPlayerData;
+ *pPlayerData = *pBotData;
+ *pBotData = tmp;
+
+ CBaseEntity *pPlayerBaseEnt = CBaseEntity::Instance( pPlayerData );
+ CBaseEntity *pBotBaseEnt = CBaseEntity::Instance( pBotData );
+
+ // Make the other a bot and make the player not a bot.
+ pPlayerBaseEnt->RemoveFlag( FL_FAKECLIENT );
+ pBotBaseEnt->AddFlag( FL_FAKECLIENT );
+
+ // Point the CBaseEntities at the right players.
+ pPlayerBaseEnt->NetworkProp()->SetEdict( pPlayerData );
+ pBotBaseEnt->NetworkProp()->SetEdict( pBotData );
+
+ // Freeze the bot.
+ pBotBaseEnt->AddEFlags( EFL_BOT_FROZEN );
+
+ // Remove orders to both of them..
+ CTFTeam *pTeam = pPlayer->GetTFTeam();
+ if ( pTeam )
+ {
+ pTeam->RemoveOrdersToPlayer( (CBaseTFPlayer*)pPlayerBaseEnt );
+ pTeam->RemoveOrdersToPlayer( (CBaseTFPlayer*)pBotBaseEnt );
+ }
+ }
+ }
+ }
+
+
+ // Handler for the "bot" command.
+ CON_COMMAND_F( "bot", "Add a bot.", FCVAR_CHEAT )
+ {
+ CBaseTFPlayer *pPlayer = CBaseTFPlayer::Instance( UTIL_GetCommandClientIndex() );
+
+ // The bot command uses switches like command-line switches.
+ // -count <count> tells how many bots to spawn.
+ // -team <index> selects the bot's team. Default is -1 which chooses randomly.
+ // Note: if you do -team !, then it
+ // -class <index> selects the bot's class. Default is -1 which chooses randomly.
+ // -frozen prevents the bots from running around when they spawn in.
+
+ // Look at -count.
+ int count = args.FindArgInt( "-count", 1 );
+ count = clamp( count, 1, 16 );
+
+ int iTeam = -1;
+ const char *pVal = args.FindArg( "-team" );
+ if ( pVal )
+ {
+ if ( pVal[0] == '!' )
+ {
+ iTeam = pPlayer->GetTFTeam()->GetEnemyTeam()->GetTeamNumber();
+ }
+ else
+ {
+ iTeam = atoi( pVal );
+ iTeam = clamp( iTeam, 0, (GetNumberOfTeams()-1) );
+ }
+ }
+
+ int iClass = args.FindArgInt( "-class", -1 );
+ iClass = clamp( iClass, -1, TFCLASS_CLASS_COUNT );
+ if ( iClass == TFCLASS_UNDECIDED )
+ iClass = TFCLASS_RECON;
+
+ // Look at -frozen.
+ bool bFrozen = !!args.FindArg( "-frozen" );
+
+ // Ok, spawn all the bots.
+ while ( --count >= 0 )
+ {
+ BotPutInServer( bFrozen, iTeam, iClass );
+ }
+ }
+
+
+ ConCommand cc_PossessBot( "PossessBot", PossessBot_f, "Toggle. Possess a bot.\n\tArguments: <bot client number>", FCVAR_CHEAT );
+
+
+ Vector MaybeDropToGround(
+ CBaseEntity *pMainEnt,
+ bool bDropToGround,
+ const Vector &vPos,
+ const Vector &vMins,
+ const Vector &vMaxs )
+ {
+ if ( bDropToGround )
+ {
+ trace_t trace;
+ UTIL_TraceHull( vPos, vPos + Vector( 0, 0, -500 ), vMins, vMaxs, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &trace );
+ return trace.endpos;
+ }
+ else
+ {
+ return vPos;
+ }
+ }
+
+
+ //-----------------------------------------------------------------------------
+ // Purpose: This function can be used to find a valid placement location for an entity.
+ // Given an origin to start looking from and a minimum radius to place the entity at,
+ // it will sweep out a circle around vOrigin and try to find a valid spot (on the ground)
+ // where mins and maxs will fit.
+ // Input : *pMainEnt - Entity to place
+ // &vOrigin - Point to search around
+ // fRadius - Radius to search within
+ // nTries - Number of tries to attempt
+ // &mins - mins of the Entity
+ // &maxs - maxs of the Entity
+ // &outPos - Return point
+ // Output : Returns true and fills in outPos if it found a spot.
+ //-----------------------------------------------------------------------------
+ bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround )
+ {
+ // This function moves the box out in each dimension in each step trying to find empty space like this:
+ //
+ // X
+ // X X
+ // Step 1: X Step 2: XXX Step 3: XXXXX
+ // X X
+ // X
+ //
+
+ Vector mins, maxs;
+ pMainEnt->CollisionProp()->WorldSpaceAABB( &mins, &maxs );
+ mins -= pMainEnt->GetAbsOrigin();
+ maxs -= pMainEnt->GetAbsOrigin();
+
+ // Put some padding on their bbox.
+ float flPadSize = 5;
+ Vector vTestMins = mins - Vector( flPadSize, flPadSize, flPadSize );
+ Vector vTestMaxs = maxs + Vector( flPadSize, flPadSize, flPadSize );
+
+ // First test the starting origin.
+ if ( UTIL_IsSpaceEmpty( pMainEnt, vOrigin + vTestMins, vOrigin + vTestMaxs ) )
+ {
+ outPos = MaybeDropToGround( pMainEnt, bDropToGround, vOrigin, vTestMins, vTestMaxs );
+ return true;
+ }
+
+ Vector vDims = vTestMaxs - vTestMins;
+
+
+ // Keep branching out until we get too far.
+ int iCurIteration = 0;
+ int nMaxIterations = 15;
+
+ int offset = 0;
+ do
+ {
+ for ( int iDim=0; iDim < 3; iDim++ )
+ {
+ float flCurOffset = offset * vDims[iDim];
+
+ for ( int iSign=0; iSign < 2; iSign++ )
+ {
+ Vector vBase = vOrigin;
+ vBase[iDim] += (iSign*2-1) * flCurOffset;
+
+ if ( UTIL_IsSpaceEmpty( pMainEnt, vBase + vTestMins, vBase + vTestMaxs ) )
+ {
+ // Ensure that there is a clear line of sight from the spawnpoint entity to the actual spawn point.
+ // (Useful for keeping things from spawning behind walls near a spawn point)
+ trace_t tr;
+ UTIL_TraceLine( vOrigin, vBase, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &tr );
+
+ if ( tr.fraction != 1.0 )
+ {
+ continue;
+ }
+
+ outPos = MaybeDropToGround( pMainEnt, bDropToGround, vBase, vTestMins, vTestMaxs );
+ return true;
+ }
+ }
+ }
+
+ ++offset;
+ } while ( iCurIteration++ < nMaxIterations );
+
+ // Warning( "EntityPlacementTest for ent %d:%s failed!\n", pMainEnt->entindex(), pMainEnt->GetClassname() );
+ return false;
+ }
+
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ CBaseEntity *CTeamFortress::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
+ {
+ CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint();
+
+ // Make sure the spawn spot isn't blocked...
+ Vector vecTestOrg = pSpawnSpot->GetAbsOrigin();
+
+ vecTestOrg.z += pPlayer->WorldAlignSize().z * 0.5;
+ Vector origin;
+ EntityPlacementTest( pPlayer, vecTestOrg, origin, true );
+
+ // Move the player to the place it said.
+ pPlayer->Teleport( &origin, NULL, NULL );
+
+ pPlayer->SetAbsVelocity( vec3_origin );
+ pPlayer->SetLocalAngles( pSpawnSpot->GetLocalAngles() );
+ pPlayer->m_Local.m_vecPunchAngle = vec3_angle;
+ pPlayer->SnapEyeAngles( pSpawnSpot->GetLocalAngles() );
+
+ return pSpawnSpot;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ CTeamFortress::CTeamFortress()
+ {
+ m_bAllowWeaponSwitch = true;
+
+ // Create the team managers
+ for ( int i = 0; i < MAX_TF_TEAMS; i++ )
+ {
+ CTFTeam *pTeam = static_cast<CTFTeam*>(CreateEntityByName( "tf_team_manager" ));
+ pTeam->Init( sTeamNames[i], i );
+
+ g_Teams.AddToTail( pTeam );
+ }
+
+ // Create the hint manager
+ CBaseEntity::Create( "tf_hintmanager", vec3_origin, vec3_angle );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ CTeamFortress::~CTeamFortress()
+ {
+ // Note, don't delete each team since they are in the gEntList and will
+ // automatically be deleted from there, instead.
+ g_Teams.Purge();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ void CTeamFortress::UpdateClientData( CBasePlayer *player )
+ {
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Called after every level and load change
+ //-----------------------------------------------------------------------------
+ void CTeamFortress::LevelInitPostEntity()
+ {
+ g_flNextReinforcementTime = gpGlobals->curtime + REINFORCEMENT_TIME;
+ BaseClass::LevelInitPostEntity();
+ }
+
+ void CTeamFortress::CreateStandardEntities()
+ {
+ // Create the player resource
+ g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "tf_player_manager", vec3_origin, vec3_angle );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: The gamerules think function
+ //-----------------------------------------------------------------------------
+ void CTeamFortress::Think( void )
+ {
+ BaseClass::Think();
+
+ // Check the reinforcement time
+ if ( g_flNextReinforcementTime <= gpGlobals->curtime )
+ {
+ //Msg( "Reinforcement Tick\n" );
+
+ // Reinforce any dead players
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( UTIL_PlayerByIndex(i) );
+ if ( pPlayer )
+ {
+ // Ready to respawn?
+ if ( pPlayer->IsReadyToReinforce() )
+ {
+ pPlayer->Reinforce();
+ //pPlayer->GetTFTeam()->PostMessage( TEAMMSG_REINFORCEMENTS_ARRIVED );
+ }
+ }
+ }
+
+ g_flNextReinforcementTime += REINFORCEMENT_TIME;
+ }
+
+ // Tell each Team to think
+ for ( int i = 0; i < GetNumberOfTeams(); i++ )
+ {
+ GetGlobalTeam( i )->Think();
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Player has just left the game
+ //-----------------------------------------------------------------------------
+ void CTeamFortress::ClientDisconnected( edict_t *pClient )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)CBaseEntity::Instance( pClient );
+ if ( pPlayer )
+ {
+ // Tell all orders that this player's left
+ COrderEvent_PlayerDisconnected order( pPlayer );
+ GlobalOrderEvent( &order );
+
+ // Delete this player's playerclass
+ pPlayer->ClearPlayerClass();
+ }
+
+ BaseClass::ClientDisconnected( pClient );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: TF2 Specific Client Commands
+ // Input :
+ // Output :
+ //-----------------------------------------------------------------------------
+ bool CTeamFortress::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pEdict;
+
+ const char *pcmd = args[0];
+ if ( FStrEq( pcmd, "objcmd" ) )
+ {
+ if ( args.ArgC() < 3 )
+ return true;
+
+ int entindex = atoi( args[1] );
+ edict_t* pEdict = INDEXENT(entindex);
+ if (pEdict)
+ {
+ CBaseEntity* pBaseEntity = GetContainingEntity(pEdict);
+ CBaseObject* pObject = dynamic_cast<CBaseObject*>(pBaseEntity);
+ if (pObject && pObject->InSameTeam(pPlayer))
+ {
+ // We have to be relatively close to the object too...
+
+ // FIXME: When I put in a better dismantle solution (namely send the dismantle
+ // command along with a cancledismantle command), re-enable this.
+ // Also, need to solve the problem of control panels on large objects
+ // For the battering ram, for instance, this distance is too far.
+
+ // float flDistSq = pObject->GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() );
+ // if (flDistSq <= (MAX_OBJECT_COMMAND_DISTANCE * MAX_OBJECT_COMMAND_DISTANCE))
+ {
+ CCommand objectArgs( args.ArgC() - 2, &args.ArgV()[2]);
+ pObject->ClientCommand( pPlayer, objectArgs );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ if ( FStrEq( pcmd, "buildvehicle" ) )
+ {
+ if ( args.ArgC() < 3 )
+ return true;
+
+ int entindex = atoi( args[1] );
+ int ivehicle = atoi( args[2] );
+ edict_t *pEdict = INDEXENT(entindex);
+ if (pEdict)
+ {
+ CBaseEntity *pBaseEntity = GetContainingEntity(pEdict);
+ CVGuiScreenVehicleBay *pBayScreen = dynamic_cast<CVGuiScreenVehicleBay*>(pBaseEntity);
+ if ( pBayScreen && pBayScreen->InSameTeam(pPlayer) )
+ {
+ // Need the same logic as objcmd above to ensure the player's near the vehicle bay vgui screen
+ pBayScreen->BuildVehicle( pPlayer, ivehicle );
+ }
+ }
+
+ return true;
+ }
+
+ // TF Commands
+ if ( FStrEq( pcmd, "menuselect" ) )
+ {
+ if ( pPlayer->m_pCurrentMenu == NULL )
+ return true;
+ if ( args.ArgC() < 2 )
+ return true;
+
+ int slot = atoi( args[1] );
+
+ // select the item from the current menu
+ if ( pPlayer->m_pCurrentMenu->Input( pPlayer, slot ) == false )
+ {
+ // invalid selection, force menu refresh
+ pPlayer->m_MenuUpdateTime = gpGlobals->curtime;
+ pPlayer->m_MenuRefreshTime = gpGlobals->curtime;
+ }
+ return true;
+ }
+ else if ( FStrEq( pcmd, "changeclass" ) )
+ {
+ pPlayer->m_pCurrentMenu = gMenus[MENU_CLASS];
+ pPlayer->m_MenuUpdateTime = gpGlobals->curtime;
+ pPlayer->m_MenuRefreshTime = gpGlobals->curtime;
+ return true;
+ }
+ else if ( FStrEq( pcmd, "changeteam" ) )
+ {
+ pPlayer->m_pCurrentMenu = gMenus[MENU_TEAM];
+ pPlayer->m_MenuUpdateTime = gpGlobals->curtime;
+ pPlayer->m_MenuRefreshTime = gpGlobals->curtime;
+ return true;
+ }
+ else if ( FStrEq( pcmd, "tactical" ) )
+ {
+ bool bTactical = args[1][0] == '!' ? !pPlayer->GetLocalData()->m_nInTacticalView : (atoi( args[1] ) ? true : false);
+
+ pPlayer->ShowTacticalView( bTactical );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "tech" ) )
+ {
+ CTFTeam *pTFTeam = pPlayer->GetTFTeam();
+ if ( !pTFTeam )
+ return true;
+
+ if ( args.ArgC() == 2 )
+ {
+ const char *name = args[1];
+
+ CBaseTechnology *tech = pTFTeam->m_pTechnologyTree->GetTechnology( name );
+ if ( tech )
+ {
+ pTFTeam->EnableTechnology( tech );
+ }
+ }
+ else
+ {
+ Msg( "usage: tech <name>\n" );
+ }
+ return true;
+ }
+ else if ( FStrEq( pcmd, "techall" ) )
+ {
+ if ( pPlayer->GetTFTeam() )
+ {
+ pPlayer->GetTFTeam()->EnableAllTechnologies();
+ }
+ return true;
+ }
+ else if ( FStrEq( pcmd, "tank" ) )
+ {
+ CBaseEntity::Create( "tank", pPlayer->WorldSpaceCenter(), pPlayer->GetLocalAngles() );
+ }
+ else if ( FStrEq( pcmd, "addres" ) || FStrEq( pcmd, "ar" ) )
+ {
+ if ( args.ArgC() == 3 )
+ {
+ int team = atoi( args[1] );
+ float flResourceAmount = atof( args[2] );
+ if ( team >= 0 && team < GetNumberOfTeams() )
+ {
+ GetGlobalTFTeam( team )->AddTeamResources( flResourceAmount );
+ }
+ }
+ else
+ {
+ Msg( "usage: ar <team 1 : 2> <amount>\n" );
+ }
+ return true;
+ }
+ else if ( FStrEq( pcmd, "preftech" ) )
+ {
+ CTFTeam *pTFTeam = pPlayer->GetTFTeam();
+ if ( !pTFTeam )
+ return true;
+
+ if ( args.ArgC() == 2 )
+ {
+ int iPrefTechIndex = atoi( args[1] );
+
+ pPlayer->SetPreferredTechnology( pTFTeam->m_pTechnologyTree, iPrefTechIndex );
+
+ }
+ return true;
+ }
+ else if( FStrEq( pcmd, "decaltest" ) )
+ {
+ trace_t trace;
+ int entityIndex;
+ Vector vForward;
+
+ AngleVectors( pEdict->GetAbsAngles(), &vForward, NULL, NULL );
+
+ UTIL_TraceLine( pEdict->GetAbsOrigin(), pEdict->GetAbsOrigin() + vForward * 10000, MASK_SOLID_BRUSHONLY, pEdict, COLLISION_GROUP_NONE, &trace );
+
+ entityIndex = trace.GetEntityIndex();
+
+ int id = UTIL_PrecacheDecal( "decals/tscorch", true );
+ CBroadcastRecipientFilter filter;
+ te->BSPDecal( filter, 0.0,
+ &trace.endpos, entityIndex, id );
+
+ return true;
+ }
+ else if( FStrEq( pcmd, "killorder" ) )
+ {
+ if( pPlayer->GetTFTeam() )
+ pPlayer->GetTFTeam()->RemoveOrdersToPlayer( pPlayer );
+
+ return true;
+ }
+ else if( BaseClass::ClientCommand( pEdict, args ) )
+ {
+ return true;
+ }
+ else
+ {
+ return pPlayer->ClientCommand( args );
+ }
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Player has just spawned. Equip them.
+ //-----------------------------------------------------------------------------
+ void CTeamFortress::PlayerSpawn( CBasePlayer *pPlayer )
+ {
+ pPlayer->EquipSuit();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ bool CTeamFortress::PlayFootstepSounds( CBasePlayer *pl )
+ {
+ if ( footsteps.GetInt() == 0 )
+ return false;
+
+ CBaseTFPlayer *tfPlayer = static_cast< CBaseTFPlayer * >( pl );
+ if ( tfPlayer )
+ {
+ if ( tfPlayer->IsKnockedDown() )
+ {
+ return false;
+ }
+ }
+
+ // only make step sounds in multiplayer if the player is moving fast enough
+ if ( pl->IsOnLadder() || pl->GetAbsVelocity().Length2D() > 100 )
+ return true;
+
+ return false;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Remove falling damage for jetpacking recons
+ //-----------------------------------------------------------------------------
+ float CTeamFortress::FlPlayerFallDamage( CBasePlayer *pPlayer )
+ {
+ int iFallDamage = (int)falldamage.GetFloat();
+
+ CBaseTFPlayer *pTFPlayer = (CBaseTFPlayer *)pPlayer;
+ if ( pTFPlayer->IsClass( TFCLASS_RECON ) )
+ return 0;
+
+ switch ( iFallDamage )
+ {
+ case 1://progressive
+ pPlayer->m_Local.m_flFallVelocity -= PLAYER_MAX_SAFE_FALL_SPEED;
+ return pPlayer->m_Local.m_flFallVelocity * DAMAGE_FOR_FALL_SPEED;
+ break;
+ default:
+ case 0:// fixed
+ return 10;
+ break;
+ }
+ }
+
+
+ //-----------------------------------------------------------------------------
+ // Is the ray blocked by enemy shields?
+ //-----------------------------------------------------------------------------
+ bool CTeamFortress::IsBlockedByEnemyShields( const Vector& src, const Vector& end, int nFriendlyTeam )
+ {
+ // Iterate over all shields on the same team, disable them so
+ // we don't intersect with them...
+ CShield::ActivateShields( false, nFriendlyTeam );
+
+ bool bBlocked = CShield::IsBlockedByShields( src, end );
+
+ CShield::ActivateShields( true, nFriendlyTeam );
+
+ return bBlocked;
+ }
+
+
+ //-----------------------------------------------------------------------------
+ // Traces a line vs a shield, returns damage reduction
+ //-----------------------------------------------------------------------------
+ float CTeamFortress::WeaponTraceEntity( CBaseEntity *pEntity,
+ const Vector &src, const Vector &end, unsigned int mask, trace_t *pTrace )
+ {
+ int damageType = pEntity->GetDamageType();
+
+ // Iterate over all shields on the same team, disable them so
+ // we don't intersect with them...
+ CShield::ActivateShields( false, pEntity->GetTeamNumber() );
+
+ // Trace it baby...
+ float damage = 1.0f;
+ bool done;
+ do
+ {
+ // FIXME: Optimize so we don't test the same ray but start at the
+ // previous collision point
+ done = true;
+ UTIL_TraceEntity( pEntity, src, end, mask, pTrace );
+
+ // Shield check...
+ if (pTrace->fraction != 1.0)
+ {
+ CBaseEntity *pCollidedEntity = pTrace->m_pEnt;
+
+ // Did we hit a shield?
+ CShield* pShield = dynamic_cast<CShield*>(pCollidedEntity);
+ if (pShield)
+ {
+ Vector vecDir;
+ VectorSubtract( end, src, vecDir );
+
+ // Let's see if we let this damage type through...
+ if (pShield->ProtectionAmount( damageType ) == 1.0f)
+ {
+ // We deflected all of the damage
+ pShield->RegisterDeflection( vecDir, damageType, pTrace );
+ damage = 0.0f;
+ }
+ else
+ {
+ // We deflected part of the damage, but we need to trace again
+ // only this time we can't let the shield register a collision
+ damage *= 1.0f - pShield->ProtectionAmount( damageType );
+
+ // FIXME: DMG_BULLET should be something else
+ pShield->RegisterPassThru( vecDir, damageType, pTrace );
+ pShield->ActivateCollisions( false );
+
+ done = false;
+ }
+ }
+ }
+ }
+ while (!done);
+
+ // Reduce the damage dealt... but don't worry about if if the
+ // shield actually deflected it. In that case, we actually want
+ // explosive things to explode at full blast power. The shield will prevent
+ // the blast damage to things behind the shield
+ if (damage != 0.0)
+ {
+ pEntity->SetDamage(pEntity->GetDamage() * damage);
+ }
+
+ // Reactivate all shields
+ CShield::ActivateShields( true );
+ return damage;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Is trace blocked by a world or a shield?
+ //-----------------------------------------------------------------------------
+ bool CTeamFortress::IsTraceBlockedByWorldOrShield( const Vector& src, const Vector& end, CBaseEntity *pShooter, int damageType, trace_t* pTrace )
+ {
+ // Iterate over all shields on the same team, disable them so
+ // we don't intersect with them...
+ CShield::ActivateShields( false, pShooter->GetTeamNumber() );
+
+ //NDebugOverlay::Line( src, pTrace->endpos, 255,255,255, true, 5.0 );
+ //NDebugOverlay::Box( pTrace->endpos, Vector(-2,-2,-2), Vector(2,2,2), 255,255,255, true, 5.0 );
+
+ // Now make sure there isn't something other than team players in the way.
+ class CShieldWorldFilter : public CTraceFilterSimple
+ {
+ public:
+ CShieldWorldFilter( CBaseEntity *pShooter ) : CTraceFilterSimple( pShooter, TFCOLLISION_GROUP_WEAPON )
+ {
+ }
+
+ virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
+ {
+ CBaseEntity *pEnt = static_cast<CBaseEntity*>(pHandleEntity);
+
+ // Did we hit a brushmodel?
+ if ( pEnt->GetSolid() == SOLID_BSP )
+ return true;
+
+ // Ignore collisions with everything but shields
+ if ( pEnt->GetCollisionGroup() != TFCOLLISION_GROUP_SHIELD )
+ return false;
+
+ return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
+ }
+ };
+
+ trace_t tr;
+ CShieldWorldFilter shieldworldFilter( pShooter );
+ UTIL_TraceLine( src, end, MASK_SOLID, &shieldworldFilter, pTrace );
+
+ // Shield check...
+ if (pTrace->fraction != 1.0)
+ {
+ CBaseEntity *pEntity = pTrace->m_pEnt;
+ CShield* pShield = dynamic_cast<CShield*>(pEntity);
+ if (pShield)
+ {
+ Vector vecDir;
+ VectorSubtract( end, src, vecDir );
+
+ // We deflected all of the damage
+ pShield->RegisterDeflection( vecDir, damageType, pTrace );
+ }
+ }
+
+ // Reactivate all shields
+ CShield::ActivateShields( true );
+ return ( pTrace->fraction < 1.0 );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Default implementation of radius damage
+ //-----------------------------------------------------------------------------
+ void CTeamFortress::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore )
+ {
+ CBaseEntity *pEntity = NULL;
+ trace_t tr;
+ float flAdjustedDamage, falloff;
+ Vector vecSpot;
+ Vector vecSrc = vecSrcIn;
+
+ if ( flRadius )
+ falloff = info.GetDamage() / flRadius;
+ else
+ falloff = 1.0;
+
+ int bInWater = (UTIL_PointContents ( vecSrc ) & MASK_WATER) ? true : false;
+
+ // in case grenade is lying on the ground
+ // Is this even needed anymore? Grenades already jump up in their explode code...
+ vecSrc.z += 1;
+
+ // iterate on all entities in the vicinity.
+ for ( CEntitySphereQuery sphere( vecSrc, flRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
+ {
+ if ( pEntity->m_takedamage != DAMAGE_NO )
+ {
+ // UNDONE: this should check a damage mask, not an ignore
+ if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
+ continue;
+
+ // blast's don't tavel into or out of water
+ if (bInWater && pEntity->GetWaterLevel() == 0)
+ continue;
+ if (!bInWater && pEntity->GetWaterLevel() == 3)
+ continue;
+
+ // Copy initial values out of the info
+ CTakeDamageInfo subInfo = info;
+ if ( !subInfo.GetAttacker() )
+ {
+ subInfo.SetAttacker( subInfo.GetInflictor() );
+ }
+
+ // Don't bother with hitboxes on this test
+ vecSpot = pEntity->WorldSpaceCenter( );
+ WeaponTraceLine ( vecSrc, vecSpot, MASK_SHOT & (~CONTENTS_HITBOX), subInfo.GetInflictor(), subInfo.GetDamageType(), &tr );
+ if ( tr.fraction != 1.0 && tr.m_pEnt != pEntity )
+ continue;
+
+ // We're going to need to see if it actually hit a shield
+ // the explosion can 'see' this entity, so hurt them!
+ if (tr.startsolid)
+ {
+ // if we're stuck inside them, fixup the position and distance
+ tr.endpos = vecSrc;
+ tr.fraction = 0.0;
+ }
+
+ // decrease damage for an ent that's farther from the bomb.
+ flAdjustedDamage = ( vecSrc - tr.endpos ).Length() * falloff;
+ flAdjustedDamage = subInfo.GetDamage() - flAdjustedDamage;
+ if ( flAdjustedDamage > 0 )
+ {
+ // Knockdown
+ // For now, just use damage. Eventually we should do it on a per-weapon basis.
+ /*
+ if ( pEntity->IsPlayer() && flAdjustedDamage > 40 )
+ {
+ Vector vecForce = vecSpot - vecSrc;
+ // Reduce the Z component and increase the X,Y
+ vecForce.x *= 3.0;
+ vecForce.y *= 3.0;
+ vecForce.z *= 0.5;
+ VectorNormalize( vecForce );
+ ((CBaseTFPlayer*)pEntity)->KnockDownPlayer( vecForce, flAdjustedDamage * 15.0f, tf_knockdowntime.GetFloat() );
+ }
+ */
+
+ // Msg( "hit %s\n", pEntity->GetClassname() );
+ subInfo.SetDamage( flAdjustedDamage );
+
+ Vector dir = tr.endpos - vecSrc;
+ if ( VectorNormalize( dir ) == 0 )
+ {
+ dir = vecSpot - vecSrc;
+ VectorNormalize( dir );
+ }
+
+ // If we don't have a damage force, manufacture one
+ if ( subInfo.GetDamagePosition() == vec3_origin || subInfo.GetDamageForce() == vec3_origin )
+ {
+ CalculateExplosiveDamageForce( &subInfo, dir, vecSrc );
+ }
+
+ if (tr.fraction != 1.0)
+ {
+ ClearMultiDamage( );
+
+ pEntity->DispatchTraceAttack( subInfo, dir, &tr );
+ ApplyMultiDamage();
+ }
+ else
+ {
+ pEntity->TakeDamage( subInfo );
+ }
+ }
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Find out if this player had an assistant when he killed an enemy
+ //-----------------------------------------------------------------------------
+ CBasePlayer *CTeamFortress::GetDeathAssistant( CBaseEntity *pKiller, CBaseEntity *pInflictor )
+ {
+ if ( !pKiller || pKiller->Classify() != CLASS_PLAYER )
+ return NULL;
+
+ CBaseTFPlayer *pAssistant = NULL;
+
+ // Killing entity might be specifying a scorer player
+ IScorer *pScorerInterface = dynamic_cast<IScorer*>( pKiller );
+ if ( pScorerInterface )
+ {
+ pAssistant = (CBaseTFPlayer*)pScorerInterface->GetAssistant();
+ }
+
+ // Inflicting entity might be specifying a scoring player
+ if ( !pAssistant )
+ {
+ pScorerInterface = dynamic_cast<IScorer*>( pInflictor );
+ if ( pScorerInterface )
+ {
+ pAssistant = (CBaseTFPlayer*)pScorerInterface->GetAssistant();
+ }
+ }
+
+ // Don't allow self assistance
+ Assert( pAssistant != pKiller );
+ return pAssistant;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : *pVictim -
+ // *pKiller -
+ // *pInflictor -
+ //-----------------------------------------------------------------------------
+ void CTeamFortress::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
+ {
+ // 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_index = 0;
+ int assist_index = 0;
+
+ // Find the killer & the scorer
+ CBaseEntity *pInflictor = info.GetInflictor();
+ CBaseEntity *pKiller = info.GetAttacker();
+ CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
+ CBasePlayer *pAssistant = GetDeathAssistant( pKiller, pInflictor );
+
+ if ( pAssistant )
+ {
+ assist_index = pAssistant->entindex();
+ }
+
+ // Custom kill type?
+ if ( info.GetDamageCustom() )
+ {
+ killer_weapon_name = GetDamageCustomString( info );
+ if ( pScorer )
+ {
+ killer_index = pScorer->entindex();
+ }
+ }
+ else
+ {
+ // Is the killer a client?
+ if ( pScorer )
+ {
+ killer_index = pScorer->entindex();
+
+ 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()->GetDeathNoticeName();
+ }
+ }
+ else
+ {
+ killer_weapon_name = STRING( pInflictor->m_iClassname ); // it's just that easy
+ }
+ }
+ }
+ else
+ {
+ killer_weapon_name = STRING( pInflictor->m_iClassname );
+ }
+
+ // 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_", 8 ) == 0 )
+ {
+ killer_weapon_name += 8;
+ }
+ else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 )
+ {
+ killer_weapon_name += 5;
+ }
+ }
+
+/* CReliableBroadcastRecipientFilter filter;
+ UserMessageBegin( filter, "DeathMsg" );
+ WRITE_BYTE( killer_index ); // the killer
+ WRITE_BYTE( assist_index ); // the assistant, if any
+ WRITE_BYTE( ENTINDEX(pVictim->edict()) ); // the victim
+ WRITE_STRING( killer_weapon_name ); // what they were killed by (should this be a string?)
+ MessageEnd();
+
+ // Did he kill himself?
+ if ( pVictim == pScorer )
+ {
+ UTIL_LogPrintf( "\"%s<%i>\" killed self with %s\n", pVictim->GetPlayerName(), engine->GetPlayerUserId( pVictim->edict() ), killer_weapon_name );
+ }
+ else if ( pScorer )
+ {
+ UTIL_LogPrintf( "\"%s<%i>\" killed \"%s<%i>\" with %s\n", pScorer->GetPlayerName() ),
+ engine->GetPlayerUserId( pScorer->edict() ),
+ pVictim->GetPlayerName(),
+ engine->GetPlayerUserId( pVictim->edict() ),
+ killer_weapon_name );
+ }
+ else
+ {
+ // killed by the world
+ UTIL_LogPrintf( "\"%s<%i>\" killed by world with %s\n", pVictim->GetPlayerName(), engine->GetPlayerUserId( pVictim->edict() ), killer_weapon_name );
+ } */
+
+ KeyValues * event = new KeyValues( "player_death" );
+ event->SetInt("killer", pScorer ? pScorer->GetUserID() : 0 );
+ event->SetInt("victim", pVictim->GetUserID() );
+ event->SetString("weapon", killer_weapon_name );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Custom kill types for TF
+ //-----------------------------------------------------------------------------
+ const char *CTeamFortress::GetDamageCustomString( const CTakeDamageInfo &info )
+ {
+ switch( info.GetDamageCustom() )
+ {
+ case DMG_KILL_BULLRUSH:
+ return "bullrush";
+ break;
+ default:
+ break;
+ };
+
+ return "INVALID CUSTOM KILL TYPE";
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : *pListener -
+ // *pSpeaker -
+ // Output : Returns true on success, false on failure.
+ //-----------------------------------------------------------------------------
+ bool CTeamFortress::PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker )
+ {
+ if ( BaseClass::PlayerCanHearChat( pListener, pSpeaker ) )
+ {
+ return true;
+ }
+
+ CBaseTFPlayer *listener = static_cast< CBaseTFPlayer * >( pListener );
+ CBaseTFPlayer *speaker = static_cast< CBaseTFPlayer * >( pSpeaker );
+
+ if ( listener && speaker )
+ {
+ if ( listener->IsClass( TFCLASS_INFILTRATOR ) )
+ {
+ Vector delta;
+ delta = listener->EarPosition() - speaker->GetAbsOrigin();
+ if ( delta.Length() < INFILTRATOR_EAVESDROP_RADIUS )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ void CTeamFortress::InitDefaultAIRelationships( void )
+ {
+ // Allocate memory for default relationships
+ CBaseCombatCharacter::AllocateDefaultRelationships();
+
+ // --------------------------------------------------------------
+ // First initialize table so we can report missing relationships
+ // --------------------------------------------------------------
+ int i, j;
+ for (i=0;i<NUM_AI_CLASSES;i++)
+ {
+ for (j=0;j<NUM_AI_CLASSES;j++)
+ {
+ // By default all relationships are neutral of priority zero
+ CBaseCombatCharacter::SetDefaultRelationship( (Class_T)i, (Class_T)j, D_NU, 0 );
+ }
+ }
+
+ // ------------------------------------------------------------
+ // > CLASS_ANTLION
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_PLAYER, D_HT, 5);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_BARNACLE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_CITIZEN_PASSIVE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_COMBINE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_MANHACK, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_METROPOLICE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_MILITARY, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_SCANNER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_STALKER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_PROTOSNIPER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_ANTLION, D_LI, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ANTLION, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_BARNACLE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_BARNACLE, D_LI, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_CITIZEN_PASSIVE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_COMBINE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_MANHACK, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_METROPOLICE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_MILITARY, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_STALKER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BARNACLE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_BULLSEYE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_ANTLION, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_CITIZEN_REBEL, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_CONSCRIPT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_HEADCRAB, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_VORTIGAUNT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_ZOMBIE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_BULLSEYE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_CITIZEN_PASSIVE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_BARNACLE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_CITIZEN_REBEL, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_CONSCRIPT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_HEADCRAB, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_MANHACK, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_MISSILE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_VORTIGAUNT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_ZOMBIE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_PASSIVE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_CITIZEN_REBEL
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_BARNACLE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_CITIZEN_REBEL, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_COMBINE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_CONSCRIPT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_MANHACK, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_METROPOLICE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_MILITARY, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_MISSILE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_SCANNER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_STALKER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_VORTIGAUNT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_ZOMBIE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CITIZEN_REBEL, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_COMBINE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_BARNACLE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_MISSILE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_COMBINE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_CONSCRIPT
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_BARNACLE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_CITIZEN_REBEL, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_COMBINE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_CONSCRIPT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_MANHACK, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_METROPOLICE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_MILITARY, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_SCANNER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_STALKER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_VORTIGAUNT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_CONSCRIPT, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_FLARE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_ANTLION, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_CITIZEN_REBEL, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_CONSCRIPT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_HEADCRAB, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_VORTIGAUNT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_ZOMBIE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_FLARE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_HEADCRAB
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_CITIZEN_PASSIVE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_COMBINE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_HEADCRAB, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_METROPOLICE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_ZOMBIE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_HEADCRAB, CLASS_EARTH_FAUNA, D_NU, 0);
+
+
+ // ------------------------------------------------------------
+ // > CLASS_MANHACK
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_CITIZEN_PASSIVE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_HEADCRAB, D_HT,-1);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MANHACK, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_METROPOLICE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_METROPOLICE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_MILITARY
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MILITARY, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_MISSILE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_MISSILE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_NONE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_ANTLION, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_CITIZEN_REBEL, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_CONSCRIPT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_HEADCRAB, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_VORTIGAUNT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_ZOMBIE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_NONE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_PLAYER
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_BARNACLE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_BULLSEYE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_CITIZEN_REBEL, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_COMBINE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_CONSCRIPT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_MANHACK, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_METROPOLICE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_MILITARY, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_SCANNER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_STALKER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_VORTIGAUNT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_PROTOSNIPER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PLAYER, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_SCANNER
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_HEADCRAB, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_ZOMBIE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_SCANNER, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_STALKER
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_HEADCRAB, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_ZOMBIE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_STALKER, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_VORTIGAUNT
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_PLAYER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_BARNACLE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_CITIZEN_PASSIVE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_CITIZEN_REBEL, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_COMBINE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_CONSCRIPT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_HEADCRAB, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_MANHACK, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_METROPOLICE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_MILITARY, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_SCANNER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_STALKER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_VORTIGAUNT, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_VORTIGAUNT, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_ZOMBIE
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_CITIZEN_PASSIVE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_COMBINE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_HEADCRAB, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_MANHACK, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_METROPOLICE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_MILITARY, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_MISSILE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_ZOMBIE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_ZOMBIE, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_PROTOSNIPER
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_NONE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_PLAYER, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_ANTLION, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_BULLSEYE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_CITIZEN_PASSIVE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_CITIZEN_REBEL, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_COMBINE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_CONSCRIPT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_FLARE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_HEADCRAB, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_MANHACK, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_METROPOLICE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_MILITARY, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_MISSILE, D_NU, 5);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_SCANNER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_STALKER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_VORTIGAUNT, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_ZOMBIE, D_HT, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_PROTOSNIPER, CLASS_EARTH_FAUNA, D_NU, 0);
+
+ // ------------------------------------------------------------
+ // > CLASS_EARTH_FAUNA
+ //
+ // Fears pretty much everything except other earth fauna:
+ //
+ // 5. Fears flares and missiles most of all.
+ // 4. Fears flying machines next.
+ // 3. Fears aliens next.
+ // 2. Fears combine human military next.
+ // 1. Fears humans next.
+ // 0. Fears the player the least.
+ //
+ // ------------------------------------------------------------
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_NONE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_PLAYER, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_ANTLION, D_FR, 2);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_BARNACLE, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_BULLSEYE, D_FR, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_CITIZEN_PASSIVE, D_FR, 1);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_CITIZEN_REBEL, D_FR, 1);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_COMBINE, D_FR, 3);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_CONSCRIPT, D_FR, 1);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_FLARE, D_FR, 5);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_HEADCRAB, D_FR, 2);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_MANHACK, D_FR, 4);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_METROPOLICE, D_FR, 1);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_MILITARY, D_FR, 1);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_MISSILE, D_FR, 5);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_SCANNER, D_FR, 4);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_STALKER, D_FR, 2);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_VORTIGAUNT, D_FR, 2);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_ZOMBIE, D_FR, 1);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_PROTOSNIPER, D_NU, 0);
+ CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_EARTH_FAUNA, D_LI, 0);
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Return a pointer to the opposing team
+ //-----------------------------------------------------------------------------
+ CTFTeam *GetOpposingTeam( CTeam *pTeam )
+ {
+ // Hacky!
+ if ( pTeam->GetTeamNumber() == 1 )
+ return GetGlobalTFTeam( 2 );
+
+ return GetGlobalTFTeam( 1 );
+ }
+
+
+ //------------------------------------------------------------------------------
+ // Purpose : Return classify text for classify type
+ //------------------------------------------------------------------------------
+ const char *CTeamFortress::AIClassText(int classType)
+ {
+ switch (classType)
+ {
+ case CLASS_NONE: return "CLASS_NONE";
+ case CLASS_PLAYER: return "CLASS_PLAYER";
+ case CLASS_ANTLION: return "CLASS_ANTLION";
+ case CLASS_BARNACLE: return "CLASS_BARNACLE";
+ case CLASS_BULLSEYE: return "CLASS_BULLSEYE";
+ case CLASS_CITIZEN_PASSIVE: return "CLASS_CITIZEN_PASSIVE";
+ case CLASS_CITIZEN_REBEL: return "CLASS_CITIZEN_REBEL";
+ case CLASS_COMBINE: return "CLASS_COMBINE";
+ case CLASS_CONSCRIPT: return "CLASS_CONSCRIPT";
+ case CLASS_HEADCRAB: return "CLASS_HEADCRAB";
+ case CLASS_MANHACK: return "CLASS_MANHACK";
+ case CLASS_METROPOLICE: return "CLASS_METROPOLICE";
+ case CLASS_MILITARY: return "CLASS_MILITARY";
+ case CLASS_SCANNER: return "CLASS_SCANNER";
+ case CLASS_STALKER: return "CLASS_STALKER";
+ case CLASS_VORTIGAUNT: return "CLASS_VORTIGAUNT";
+ case CLASS_ZOMBIE: return "CLASS_ZOMBIE";
+ case CLASS_PROTOSNIPER: return "CLASS_PROTOSNIPER";
+ case CLASS_MISSILE: return "CLASS_MISSILE";
+ case CLASS_FLARE: return "CLASS_FLARE";
+ case CLASS_EARTH_FAUNA: return "CLASS_EARTH_FAUNA";
+
+ default: return "MISSING CLASS in ClassifyText()";
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: When gaining new technologies in TF, prevent auto switching if we
+ // receive a weapon during the switch
+ // Input : *pPlayer -
+ // *pWeapon -
+ // Output : Returns true on success, false on failure.
+ //-----------------------------------------------------------------------------
+ bool CTeamFortress::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
+ {
+ if ( !GetAllowWeaponSwitch() )
+ return false;
+
+ // Never auto switch to object placement
+ if ( dynamic_cast<CWeaponBuilder*>(pWeapon) )
+ return false;
+ if ( dynamic_cast<CWeaponObjectSelection*>(pWeapon) )
+ return false;
+
+ return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : allow -
+ //-----------------------------------------------------------------------------
+ void CTeamFortress::SetAllowWeaponSwitch( bool allow )
+ {
+ m_bAllowWeaponSwitch = allow;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Output : Returns true on success, false on failure.
+ //-----------------------------------------------------------------------------
+ bool CTeamFortress::GetAllowWeaponSwitch( void )
+ {
+ return m_bAllowWeaponSwitch;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : *pPlayer -
+ // Output : const char
+ //-----------------------------------------------------------------------------
+ const char *CTeamFortress::SetDefaultPlayerTeam( CBasePlayer *pPlayer )
+ {
+ Assert( pPlayer );
+ // int clientIndex = pPlayer->entindex();
+ // engine->SetClientKeyValue( clientIndex, engine->GetInfoKeyBuffer( pPlayer->edict() ), "model", "" );
+ return BaseClass::SetDefaultPlayerTeam( pPlayer );
+ }
+
+ // Called when game rules are destroyed by CWorld
+ void CTeamFortress::LevelShutdown( void )
+ {
+ g_flNextReinforcementTime = 0.0f;
+
+ g_hCurrentAct = NULL;
+
+ BaseClass::LevelShutdown();
+ }
+
+ void InitBodyQue(void)
+ {
+ // FIXME: Make this work
+ }
+
+#endif
+
+
+// ----------------------------------------------------------------------------- //
+// Shared CTeamFortress code.
+// ----------------------------------------------------------------------------- //
+
+//-----------------------------------------------------------------------------
+// Purpose: Send the appropriate weapon impact
+//-----------------------------------------------------------------------------
+void WeaponImpact( trace_t *tr, Vector vecDir, bool bHurt, CBaseEntity *pEntity, int iDamageType )
+{
+ // If we hit a combat shield, play the hit effect
+ if ( iDamageType & (DMG_PLASMA | DMG_ENERGYBEAM) )
+ {
+ if ( bHurt )
+ {
+ Assert( pEntity );
+ bool bHitHandheldShield = (pEntity->IsPlayer() && ((CBaseTFPlayer*)pEntity)->IsHittingShield( vecDir, NULL ));
+ if ( bHitHandheldShield )
+ {
+ UTIL_ImpactTrace( tr, iDamageType, "PlasmaShield" );
+ }
+ else
+ {
+ // Client waits for server version
+#ifndef CLIENT_DLL
+ // Make sure the server sends to us, even though we're predicting
+ CDisablePredictionFiltering dpf;
+ UTIL_ImpactTrace( tr, iDamageType, "PlasmaHurt" );
+#endif
+ }
+ }
+ else
+ {
+ UTIL_ImpactTrace( tr, iDamageType, "PlasmaUnhurt" );
+ }
+ }
+ else
+ {
+ if ( bHurt )
+ {
+ Assert( pEntity );
+ bool bHitHandheldShield = (pEntity->IsPlayer() && ((CBaseTFPlayer*)pEntity)->IsHittingShield( vecDir, NULL ));
+ if ( bHitHandheldShield )
+ {
+ UTIL_ImpactTrace( tr, iDamageType, "ImpactShield" );
+ }
+ else
+ {
+ // Client waits for server version
+#ifndef CLIENT_DLL
+ // Make sure the server sends to us, even though we're predicting
+ CDisablePredictionFiltering dpf;
+ UTIL_ImpactTrace( tr, iDamageType, "Impact" );
+#endif
+ }
+ }
+ else
+ {
+ UTIL_ImpactTrace( tr, iDamageType, "ImpactUnhurt" );
+ }
+ }
+}
+
+
+static bool CheckCollisionGroupPlayerMovement( int collisionGroup0, int collisionGroup1 )
+{
+ if ( collisionGroup0 == COLLISION_GROUP_PLAYER )
+ {
+ // Players don't collide with objects or other players
+ if ( collisionGroup1 == COLLISION_GROUP_PLAYER || collisionGroup1 == TFCOLLISION_GROUP_OBJECT )
+ return false;
+ }
+
+
+ if ( collisionGroup1 == COLLISION_GROUP_PLAYER_MOVEMENT )
+ {
+ // This is only for probing, so it better not be on both sides!!!
+ Assert( collisionGroup0 != COLLISION_GROUP_PLAYER_MOVEMENT );
+
+ // No collide with players any more
+ // Nor with objects or grenades
+ switch ( collisionGroup0 )
+ {
+ default:
+ break;
+ case COLLISION_GROUP_PLAYER:
+ return false;
+ case TFCOLLISION_GROUP_OBJECT_SOLIDTOPLAYERMOVEMENT:
+ // Certain objects are still solid to player movement
+ return true;
+ case TFCOLLISION_GROUP_COMBATOBJECT:
+ case TFCOLLISION_GROUP_OBJECT:
+ return false;
+ case TFCOLLISION_GROUP_GRENADE:
+ case COLLISION_GROUP_DEBRIS:
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+void CTeamFortress::WeaponTraceLine( const Vector& src, const Vector& end, unsigned int mask, CBaseEntity *pShooter, int damageType, trace_t* pTrace )
+{
+ // Iterate over all shields on the same team, disable them so
+ // we don't intersect with them...
+ CShield::ActivateShields( false, pShooter->GetTeamNumber() );
+
+ UTIL_TraceLine(src, end, mask, pShooter, TFCOLLISION_GROUP_WEAPON, pTrace);
+
+// NDebugOverlay::Line( src, pTrace->endpos, 255,255,255, true, 5.0 );
+// NDebugOverlay::Box( pTrace->endpos, Vector(-2,-2,-2), Vector(2,2,2), 255,255,255, true, 5.0 );
+
+ // Shield check...
+ if (pTrace->fraction != 1.0)
+ {
+ CBaseEntity *pEntity = pTrace->m_pEnt;
+ CShield* pShield = dynamic_cast<CShield*>(pEntity);
+ if (pShield)
+ {
+ Vector vecDir;
+ VectorSubtract( end, src, vecDir );
+
+ // We deflected all of the damage
+ pShield->RegisterDeflection( vecDir, damageType, pTrace );
+ }
+ }
+
+ // Reactivate all shields
+ CShield::ActivateShields( true );
+}
+
+
+bool CTeamFortress::ShouldCollide( int collisionGroup0, int collisionGroup1 )
+{
+ if ( collisionGroup0 > collisionGroup1 )
+ {
+ // swap so that lowest is always first
+ swap(collisionGroup0,collisionGroup1);
+ }
+
+ // Ignore base class HL2 definition for COLLISION_GROUP_WEAPON, change to COLLISION_GROUP_NONE
+ if ( collisionGroup0 == COLLISION_GROUP_WEAPON )
+ {
+ collisionGroup0 = COLLISION_GROUP_NONE;
+ }
+ if ( collisionGroup1 == COLLISION_GROUP_WEAPON )
+ {
+ collisionGroup1 = COLLISION_GROUP_NONE;
+ }
+
+ // Shields collide with weapons + grenades only
+ if ( collisionGroup0 == TFCOLLISION_GROUP_SHIELD )
+ {
+ return ((collisionGroup1 == TFCOLLISION_GROUP_WEAPON) || (collisionGroup1 == TFCOLLISION_GROUP_GRENADE));
+ }
+
+ // Weapons can collide with things (players) in vehicles.
+ if( collisionGroup0 == COLLISION_GROUP_IN_VEHICLE && collisionGroup1 == TFCOLLISION_GROUP_WEAPON )
+ {
+ return true;
+ }
+
+ // COLLISION TEST:
+ // Players don't collide with other players or objects
+ if ( !CheckCollisionGroupPlayerMovement( collisionGroup0, collisionGroup1 ) )
+ return false;
+ // Reciprocal test
+ if ( !CheckCollisionGroupPlayerMovement( collisionGroup1, collisionGroup0 ) )
+ return false;
+
+ // Grenades don't collide with debris
+ if ( collisionGroup1 == TFCOLLISION_GROUP_GRENADE )
+ {
+ if ( collisionGroup0 == COLLISION_GROUP_DEBRIS )
+ return false;
+ }
+
+ // Combat objects don't collide with players
+ if ( collisionGroup1 == TFCOLLISION_GROUP_COMBATOBJECT )
+ {
+ if ( collisionGroup0 == COLLISION_GROUP_PLAYER )
+ return false;
+ }
+
+ // Resource chunks don't collide with each other or vehicles
+ if ( collisionGroup1 == TFCOLLISION_GROUP_RESOURCE_CHUNK )
+ {
+ if ( collisionGroup0 == TFCOLLISION_GROUP_RESOURCE_CHUNK )
+ return false;
+// if ( collisionGroup0 == COLLISION_GROUP_VEHICLE )
+// return false;
+ }
+
+ return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Fire a generic bullet
+//-----------------------------------------------------------------------------
+void CTeamFortress::FireBullets( const CTakeDamageInfo &info, int cShots, const Vector &vecSrc, const Vector &vecDirShooting,
+ const Vector &vecSpread, float flDistance, int iAmmoType,
+ int iTracerFreq, int firingEntID, int attachmentID, const char *sCustomTracer )
+{
+ static int tracerCount;
+ bool tracer;
+ trace_t tr;
+ CTakeDamageInfo subInfo = info;
+ CBaseTFCombatWeapon *pWeapon = dynamic_cast<CBaseTFCombatWeapon*>(info.GetInflictor());
+
+ Assert( subInfo.GetInflictor() );
+
+ // Default attacker is the inflictor
+ if ( subInfo.GetAttacker() == NULL )
+ {
+ subInfo.SetAttacker( subInfo.GetInflictor() );
+ }
+
+ // --------------------------------------------------
+ // Get direction vectors for spread
+ // --------------------------------------------------
+ Vector vecUp = Vector(0,0,1);
+ Vector vecRight;
+ CrossProduct ( vecDirShooting, vecUp, vecRight );
+ CrossProduct ( vecDirShooting, -vecRight, vecUp );
+
+#ifndef CLIENT_DLL
+ ClearMultiDamage();
+#endif
+
+ int seed = 0;
+
+ for (int iShot = 0; iShot < cShots; iShot++)
+ {
+ // get circular gaussian spread
+ float x, y, z;
+
+ do
+ {
+ float x1, x2, y1, y2;
+
+ // Note the additional seed because otherwise we get the same set of random #'s and will get stuck
+ // in an infinite loop here potentially
+ // FIXME: Can we use a gaussian random # function instead? ywb
+ if ( CBaseEntity::GetPredictionRandomSeed() != -1 )
+ {
+ x1 = SHARED_RANDOMFLOAT_SEED( -0.5, 0.5, ++seed );
+ x2 = SHARED_RANDOMFLOAT_SEED( -0.5, 0.5, ++seed );
+ y1 = SHARED_RANDOMFLOAT_SEED( -0.5, 0.5, ++seed );
+ y2 = SHARED_RANDOMFLOAT_SEED( -0.5, 0.5, ++seed );
+ }
+ else
+ {
+ x1 = RandomFloat( -0.5, 0.5 );
+ x2 = RandomFloat( -0.5, 0.5 );
+ y1 = RandomFloat( -0.5, 0.5 );
+ y2 = RandomFloat( -0.5, 0.5 );
+ }
+
+ x = x1 + x2;
+ y = y1 + y2;
+
+ z = x*x+y*y;
+ } while (z > 1);
+
+ Vector vecDir = vecDirShooting + x * vecSpread.x * vecRight + y * vecSpread.y * vecUp;
+ Vector vecEnd = vecSrc + vecDir * flDistance;
+
+ // Try the trace
+ WeaponTraceLine( vecSrc, vecEnd, MASK_SHOT, subInfo.GetInflictor(), subInfo.GetDamageType(), &tr );
+
+ tracer = false;
+ if (iTracerFreq != 0 && (tracerCount++ % iTracerFreq) == 0)
+ {
+ Vector vecTracerSrc;
+
+ // adjust tracer position for player
+ if ( subInfo.GetInflictor()->IsPlayer() )
+ {
+ Vector forward, right;
+ CBasePlayer *pPlayer = ToBasePlayer( subInfo.GetInflictor() );
+ pPlayer->EyeVectors( &forward, &right, NULL );
+ vecTracerSrc = vecSrc + Vector ( 0 , 0 , -4 ) + right * 2 + forward * 16;
+ }
+ else
+ {
+ vecTracerSrc = vecSrc;
+ }
+
+ if ( iTracerFreq != 1 ) // guns that always trace also always decal
+ tracer = true;
+
+ if ( sCustomTracer )
+ {
+ UTIL_Tracer( vecTracerSrc, tr.endpos, subInfo.GetInflictor()->entindex(), TRACER_DONT_USE_ATTACHMENT, 0, false, (char*)sCustomTracer );
+ }
+ else
+ {
+ UTIL_Tracer( vecTracerSrc, tr.endpos, subInfo.GetInflictor()->entindex() );
+ }
+ }
+
+ // do damage, paint decals
+ if ( tr.fraction != 1.0 )
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+
+ // NOTE: If we want to know more than whether or not the entity can actually be hurt
+ // for the purposes of impact effects, the client needs to know a lot more.
+ bool bTargetCouldBeHurt = false;
+ if ( pEntity->m_takedamage )
+ {
+ if ( !pEntity->InSameTeam( subInfo.GetInflictor() ) )
+ {
+ bTargetCouldBeHurt = true;
+ }
+#ifndef CLIENT_DLL
+ subInfo.SetDamagePosition( vecSrc );
+ // Hit the target
+ pEntity->DispatchTraceAttack( subInfo, vecDir, &tr );
+#endif
+ }
+
+ // No decal if we hit a shield
+ if ( pEntity->GetCollisionGroup() != TFCOLLISION_GROUP_SHIELD )
+ {
+ WeaponImpact( &tr, vecDir, bTargetCouldBeHurt, pEntity, subInfo.GetDamageType() );
+ }
+ }
+
+ if ( pWeapon )
+ {
+ pWeapon->BulletWasFired( vecSrc, tr.endpos );
+ }
+ }
+
+ // Apply any damage we've stacked up
+#ifndef CLIENT_DLL
+ ApplyMultiDamage();
+#endif
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Init TF2 ammo definitions
+//-----------------------------------------------------------------------------
+CAmmoDef *GetAmmoDef()
+{
+ static CAmmoDef def;
+ static bool bInitted = false;
+
+ if ( !bInitted )
+ {
+ bInitted = true;
+
+ def.AddAmmoType("Bullets", DMG_BULLET, TRACER_LINE, 0, 0, INFINITE_AMMO, 0, 0);
+ def.AddAmmoType("Rockets", DMG_BLAST, TRACER_LINE, 0, 0, 6, 0, 0);
+ def.AddAmmoType("Grenades", DMG_BLAST, TRACER_LINE, 0, 0, 3, 0, 0);
+ def.AddAmmoType("ShieldGrenades", DMG_ENERGYBEAM, TRACER_LINE, 0, 0, 5, 0, 0);
+ def.AddAmmoType("ShotgunEnergy", DMG_ENERGYBEAM, TRACER_LINE, 0, 0, INFINITE_AMMO, 0, 0);
+ def.AddAmmoType("PlasmaGrenade", DMG_ENERGYBEAM|DMG_BLAST, TRACER_LINE, 0, 0, 30, 0, 0);
+ def.AddAmmoType("ResourceChunks", DMG_GENERIC,TRACER_LINE, 0, 0, 4, 0, 0); // Resource chunks
+ def.AddAmmoType("Limpets", DMG_BLAST, TRACER_LINE, 0, 0, 40, 0, 0);
+ def.AddAmmoType("Gasoline", DMG_BURN, TRACER_LINE, 0, 0, 80, 0, 0);
+
+ // Combat Objects
+ def.AddAmmoType("RallyFlags", DMG_GENERIC, TRACER_NONE, 0, 0, 1, 0, 0);
+ def.AddAmmoType("EMPGenerators", DMG_GENERIC, TRACER_NONE, 0, 0, 1, 0, 0);
+ }
+
+ return &def;
+}
diff --git a/game/shared/tf2/tf_gamerules.h b/game/shared/tf2/tf_gamerules.h
new file mode 100644
index 0000000..9c4ce4f
--- /dev/null
+++ b/game/shared/tf2/tf_gamerules.h
@@ -0,0 +1,142 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The TF Game rules object
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_GAMERULES_H
+#define TF_GAMERULES_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "Teamplay_GameRules.h"
+#include "takedamageinfo.h"
+
+
+#ifdef CLIENT_DLL
+
+ #define CTeamFortress C_TeamFortress
+
+#endif
+
+
+class CTeamFortress : public CTeamplayRules
+{
+public:
+ DECLARE_CLASS( CTeamFortress, CTeamplayRules );
+
+ int DefaultFOV( void ) { return 90; }
+
+ // Shared implementation between client and server.
+ void WeaponTraceLine( const Vector& src, const Vector& end, unsigned int mask, CBaseEntity *pShooter, int damageType, trace_t* pTrace );
+
+ virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 );
+
+ virtual void FireBullets( const CTakeDamageInfo &info, int cShots, const Vector &vecSrc, const Vector &vecDirShooting,
+ const Vector &vecSpread, float flDistance, int iBulletType, int iTracerFreq, int firingEntID,
+ int attachmentID, const char *sCustomTracer = NULL );
+
+
+#ifdef CLIENT_DLL
+
+
+#else
+
+ CTeamFortress();
+ virtual ~CTeamFortress();
+
+ CBaseEntity *GetPlayerSpawnSpot( CBasePlayer *pPlayer );
+
+ virtual void Think( void );
+ virtual void LevelInitPostEntity( void );
+
+ virtual void CreateStandardEntities();
+
+ // Called when game rules are destroyed by CWorld
+ virtual void LevelShutdown( void );
+
+ virtual void ClientDisconnected( edict_t *pClient );
+ virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args );
+ virtual void PlayerSpawn( CBasePlayer *pPlayer );
+
+ virtual bool PlayTextureSounds( void ) { return true; }
+ virtual bool PlayFootstepSounds( CBasePlayer *pl );
+ virtual float FlPlayerFallDamage( CBasePlayer *pPlayer );
+ virtual void RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore );
+ // Let the game rules specify if fall death should fade screen to black
+ virtual bool FlPlayerFallDeathDoesScreenFade( CBasePlayer *pl ) { return FALSE; }
+
+ bool IsTraceBlockedByWorldOrShield( const Vector& src, const Vector& end, CBaseEntity *pShooter, int damageType, trace_t* pTrace );
+
+ virtual float WeaponTraceEntity( CBaseEntity *pEntity, const Vector &vecStart, const Vector &vecEnd, unsigned int mask, trace_t *ptr );
+
+ virtual void UpdateClientData( CBasePlayer *pl );
+
+ // Death notices
+ virtual void DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info );
+ virtual const char *GetDamageCustomString( const CTakeDamageInfo &info );
+ CBasePlayer *GetDeathAssistant( CBaseEntity *pKiller, CBaseEntity *pInflictor );
+
+ virtual bool PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker );
+ virtual void InitDefaultAIRelationships( void );
+
+ virtual const char *GetGameDescription( void ) { return "TeamFortress 2"; } // this is the game name that gets seen in the server browser
+ virtual const char *AIClassText(int classType);
+
+ virtual bool FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon );
+
+ virtual const char *SetDefaultPlayerTeam( CBasePlayer *pPlayer );
+
+ // Is the ray blocked by enemy shields?
+ bool IsBlockedByEnemyShields( const Vector& src, const Vector& end, int nFriendlyTeam );
+
+public:
+
+ virtual void SetAllowWeaponSwitch( bool allow );
+ virtual bool GetAllowWeaponSwitch( void );
+private:
+
+ // Don't allow switching weapons while gaining new technologies
+ bool m_bAllowWeaponSwitch;
+
+#endif
+
+};
+
+//-----------------------------------------------------------------------------
+// Gets us at the team fortress game rules
+//-----------------------------------------------------------------------------
+
+inline CTeamFortress* TFGameRules()
+{
+ #ifdef _DEBUG
+ Assert( dynamic_cast< CTeamFortress* >( g_pGameRules ) );
+ #endif
+
+ return static_cast<CTeamFortress*>(g_pGameRules);
+}
+
+// Send the appropriate weapon impact.
+void WeaponImpact( trace_t *tr, Vector vecDir, bool bHurt, CBaseEntity *pEntity, int iDamageType );
+
+
+#ifdef CLIENT_DLL
+
+#else
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Useful utility functions
+ //-----------------------------------------------------------------------------
+ class CTFTeam;
+ CTFTeam *GetOpposingTeam( CTeam *pTeam );
+ bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround );
+
+#endif
+
+#endif // TF_GAMERULES_H
diff --git a/game/shared/tf2/tf_hints.h b/game/shared/tf2/tf_hints.h
new file mode 100644
index 0000000..dce1561
--- /dev/null
+++ b/game/shared/tf2/tf_hints.h
@@ -0,0 +1,34 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_HINTS_H
+#define TF_HINTS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+enum
+{
+ TF_HINT_UNDEFINED = 0,
+ TF_HINT_VOTEFORTECHNOLOGY,
+ TF_HINT_BUILDRESOURCEPUMP,
+ TF_HINT_BUILDRESOURCEBOX,
+ TF_HINT_BUILDZONEINCREASER,
+ TF_HINT_BUILDSENTRYGUN_PLASMA,
+ TF_HINT_BUILDSANDBAG,
+ TF_HINT_BUILDANTIMORTAR,
+ TF_HINT_REPAIROBJECT,
+
+ TF_HINT_WEAPONRECEIVED,
+
+ TF_HINT_NEWTECHNOLOGY,
+
+ // Must be at end
+ TF_HINT_LASTHINT,
+};
+
+#endif // TF_HINTS_H
diff --git a/game/shared/tf2/tf_movedata.h b/game/shared/tf2/tf_movedata.h
new file mode 100644
index 0000000..75f1462
--- /dev/null
+++ b/game/shared/tf2/tf_movedata.h
@@ -0,0 +1,71 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_MOVEDATA_H
+#define TF_MOVEDATA_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "igamemovement.h"
+#include "tfclassdata_shared.h"
+
+class CPlayerClassData;
+
+// This class contains TF-specific prediction data. CMoveData can be casted to this class in
+// CTFPlayerMove and CTFGameMovement to do TF-specific movement.
+class CTFMoveData : public CMoveData
+{
+public:
+
+ Vector m_vecPosDelta;
+
+ // Revisit this!!!
+ enum { MOMENTUM_MAXSIZE = 10 };
+ float m_aMomentum[MOMENTUM_MAXSIZE];
+ int m_iMomentumHead;
+
+ int m_nClassID;
+
+ inline PlayerClassCommandoData_t &CommandoData() { return m_CommandoData; }
+ inline PlayerClassDefenderData_t &DefenderData() { return m_DefenderData; }
+ inline PlayerClassEscortData_t &EscortData() { return m_EscortData; }
+ inline PlayerClassInfiltratorData_t &InfiltratorData() { return m_InfiltratorData; }
+ inline PlayerClassMedicData_t &MedicData() { return m_MedicData; }
+ inline PlayerClassReconData_t &ReconData() { return m_ReconData; }
+ inline PlayerClassSniperData_t &SniperData() { return m_SniperData; }
+ inline PlayerClassSupportData_t &SupportData() { return m_SupportData; }
+ inline PlayerClassSapperData_t &SapperData() { return m_SapperData; }
+ inline PlayerClassPyroData_t &PyroData() { return m_PyroData; }
+ inline void* VehicleData() { return m_VehicleData; }
+ inline int VehicleDataMaxSize()
+ {
+ return VEHICLE_DATA_SIZE;
+ }
+
+private:
+ enum
+ {
+ VEHICLE_DATA_SIZE = 256
+ };
+
+ PlayerClassCommandoData_t m_CommandoData;
+ PlayerClassDefenderData_t m_DefenderData;
+ PlayerClassEscortData_t m_EscortData;
+ PlayerClassInfiltratorData_t m_InfiltratorData;
+ PlayerClassMedicData_t m_MedicData;
+ PlayerClassReconData_t m_ReconData;
+ PlayerClassSniperData_t m_SniperData;
+ PlayerClassSupportData_t m_SupportData;
+ PlayerClassSapperData_t m_SapperData;
+ PlayerClassPyroData_t m_PyroData;
+
+ unsigned char m_VehicleData[VEHICLE_DATA_SIZE];
+};
+
+
+#endif // TF_MOVEDATA_H
diff --git a/game/shared/tf2/tf_obj_base_manned_gun.cpp b/game/shared/tf2/tf_obj_base_manned_gun.cpp
new file mode 100644
index 0000000..6d3f015
--- /dev/null
+++ b/game/shared/tf2/tf_obj_base_manned_gun.cpp
@@ -0,0 +1,680 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A stationary gun that players can man
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "tf_obj_base_manned_gun.h"
+#include "tf_obj_manned_plasmagun_shared.h"
+#include "in_buttons.h"
+#include "tf_movedata.h"
+#include "tf_gamerules.h"
+
+#if defined( CLIENT_DLL )
+#include "hudelement.h"
+#include "bone_setup.h"
+#include "hud_ammo.h"
+#include "hud_crosshair.h"
+#else
+#endif
+
+IMPLEMENT_NETWORKCLASS_ALIASED( ObjectBaseMannedGun, DT_ObjectBaseMannedGun )
+
+BEGIN_NETWORK_TABLE( CObjectBaseMannedGun, DT_ObjectBaseMannedGun )
+#if !defined( CLIENT_DLL )
+ SendPropInt (SENDINFO(m_nMoveStyle), 2, SPROP_UNSIGNED ),
+ SendPropInt (SENDINFO(m_nAmmoType), 8 ),
+ SendPropInt (SENDINFO(m_nAmmoCount), 6, SPROP_UNSIGNED ),
+ SendPropAngle(SENDINFO(m_flGunYaw), 12 ),
+ SendPropAngle(SENDINFO(m_flGunPitch), 12 ),
+ SendPropAngle(SENDINFO(m_flBarrelPitch), 12 ),
+
+ SendPropEHandle( SENDINFO( m_hLaserDesignation ) ),
+ SendPropEHandle( SENDINFO( m_hBeam ) ),
+
+#else
+ RecvPropInt( RECVINFO(m_nMoveStyle) ),
+ RecvPropInt( RECVINFO(m_nAmmoType) ),
+ RecvPropInt( RECVINFO(m_nAmmoCount) ),
+ RecvPropFloat( RECVINFO(m_flGunYaw) ),
+ RecvPropFloat( RECVINFO(m_flGunPitch) ),
+ RecvPropFloat( RECVINFO(m_flBarrelPitch) ),
+
+ RecvPropEHandle( RECVINFO( m_hLaserDesignation ) ),
+ RecvPropEHandle( RECVINFO( m_hBeam ) ),
+
+#endif
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( tf_obj_base_manned_gun, CObjectBaseMannedGun );
+
+BEGIN_PREDICTION_DATA( CObjectBaseMannedGun )
+
+ DEFINE_PRED_FIELD( m_nMoveStyle, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_nAmmoType, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_nAmmoCount, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_FIELD_TOL( m_flGunYaw, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.5f),
+ DEFINE_PRED_FIELD_TOL( m_flGunPitch, FIELD_FLOAT, FTYPEDESC_INSENDTABLE | FTYPEDESC_NOERRORCHECK, 0.125f ),
+ DEFINE_PRED_FIELD_TOL( m_flBarrelPitch, FIELD_FLOAT, FTYPEDESC_INSENDTABLE | FTYPEDESC_NOERRORCHECK, 0.125f ),
+
+ DEFINE_PRED_FIELD( m_hLaserDesignation, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_FIELD( m_hBeam, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_FIELD( m_flBarrelHeight, FIELD_FLOAT ),
+
+// DEFINE_FIELD( m_nBarrelAttachment, FIELD_INTEGER ),
+// DEFINE_FIELD( m_nBarrelPivotAttachment, FIELD_INTEGER ),
+// DEFINE_FIELD( m_nStandAttachment, FIELD_INTEGER ),
+// DEFINE_FIELD( m_nEyesAttachment, FIELD_INTEGER ),
+
+END_PREDICTION_DATA()
+
+extern ConVar mannedgun_usethirdperson;
+static ConVar obj_manned_gun_designator_range( "obj_manned_gun_designator_range","2048", FCVAR_REPLICATED, "Manned gun's laser designation range" );
+ConVar obj_child_range_factor( "obj_child_range_factor","1.1", FCVAR_REPLICATED, "Factor applied to range of objects that are built on a buildpoint" );
+
+// Restoring initial state handling
+#define OBJ_BASE_MANNEDGUN_THINK_CONTEXT "BaseMannedGunThink"
+#define MANNEDGUN_RESTORE_TIME 5.0
+#define MANNEDGUN_RESTORE_TURN_RATE 150
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CObjectBaseMannedGun::CObjectBaseMannedGun()
+{
+ m_nMoveStyle = MOVEMENT_STYLE_STANDARD;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the movement style
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::SetMovementStyle( MovementStyle_t style )
+{
+ m_nMoveStyle = style;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::Precache()
+{
+ BaseClass::Precache();
+#if !defined( CLIENT_DLL )
+ PrecacheVGuiScreen( "screen_obj_manned_plasmagun" );
+ PrecacheMaterial( "sprites/laserbeam" );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::Spawn()
+{
+ m_takedamage = DAMAGE_YES;
+
+ SetMaxPassengerCount( 1 );
+
+ m_flGunYaw = 0;
+ m_flGunPitch = 0;
+ m_flBarrelPitch = 0;
+
+ BaseClass::Spawn();
+
+ // Manned guns don't need to be built like other vehicles
+ int curFlags = GetObjectFlags();
+ curFlags &= ~OF_MUST_BE_BUILT_IN_CONSTRUCTION_YARD;
+ curFlags &= ~OF_MUST_BE_BUILT_ON_ATTACHMENT;
+ curFlags &= ~OF_DOESNT_NEED_POWER;
+ curFlags |= OF_DONT_PREVENT_BUILD_NEAR_OBJ;
+ SetObjectFlags( curFlags );
+
+ m_flMaxRange = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the max range of this gun
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::CalculateMaxRange( float flDefensiveRange, float flOffensiveRange )
+{
+ if ( GetTeamNumber() == TEAM_HUMANS )
+ {
+ m_flMaxRange = flDefensiveRange;
+ if ( GetParentObject() )
+ {
+ m_flMaxRange *= obj_child_range_factor.GetFloat();
+ }
+ }
+ else
+ {
+ m_flMaxRange = flOffensiveRange;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Sets up various attachment points once the model is selected
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::OnModelSelected()
+{
+ m_nBarrelAttachment = LookupAttachment( "barrel" );
+ m_nBarrelPivotAttachment = LookupAttachment( "barrelpivot" );
+ m_nStandAttachment = LookupAttachment( "vehicle_feet_passenger0" );
+ m_nEyesAttachment = LookupAttachment( "vehicle_eyes_passenger0" );
+
+ // Find the barrel height in its quiescent state...
+ Vector vBarrel;
+ QAngle vBarrelAngles;
+ GetAttachmentLocal( m_nBarrelAttachment, vBarrel, vBarrelAngles );
+ m_flBarrelHeight = vBarrel.z;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::UpdateOnRemove( void )
+{
+ if ( m_hLaserDesignation.Get() )
+ {
+ m_hLaserDesignation->Remove( );
+ m_hLaserDesignation = NULL;
+ }
+
+ // Chain at end to mimic destructor unwind order
+ BaseClass::UpdateOnRemove();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets info about the control panels
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
+{
+ pPanelName = "screen_obj_manned_plasmagun";
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Hide the base of the gun if it's on an attachment
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::SetupAttachedVersion( void )
+{
+ BaseClass::SetupAttachedVersion();
+
+ SetBodygroup( 1, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::SetupUnattachedVersion( void )
+{
+ BaseClass::SetupUnattachedVersion();
+
+ SetBodygroup( 1, false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::OnGoInactive( void )
+{
+ BaseClass::OnGoInactive();
+
+ // If we've got a player in the gun, tell him he's got to get out
+ if ( GetDriverPlayer() )
+ {
+ ClientPrint( GetDriverPlayer(), HUD_PRINTCENTER, "Lost power to the manned gun!" );
+ GetDriverPlayer()->LeaveVehicle();
+ }
+
+#if 0
+ if ( GetBuffStation() )
+ {
+ GetBuffStation()->DeBuffObject( this );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Can we get into the vehicle?
+//-----------------------------------------------------------------------------
+bool CObjectBaseMannedGun::CanGetInVehicle( CBaseTFPlayer *pPlayer )
+{
+ if ( !IsPowered() )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "No power source for the manned gun!" );
+ return false;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the eye position
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::GetVehicleViewPosition( int nRole, Vector *pOrigin, QAngle *pAngles, float *pFOV /*= NULL*/ )
+{
+ BaseClass::GetVehicleViewPosition( nRole, pOrigin, pAngles, pFov );
+ return;
+ Assert( nRole == VEHICLE_DRIVER );
+ QAngle vPlayerFeetAngles;
+ GetAttachment(m_nEyesAttachment, *pOrigin, vPlayerFeetAngles);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return to our original facing after a while
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::BaseMannedGunThink( void )
+{
+ // If someone's got in the gun, stop moving
+ if ( GetDriverPlayer() )
+ return;
+
+ // Otherwise, move back towards the initial state
+ if ( m_flGunPitch )
+ {
+ float flPitch = anglemod( m_flGunPitch );
+ if (( flPitch <= 180 ) && ( flPitch >= 0 ))
+ {
+ m_flGunPitch = MAX( 0, flPitch - (gpGlobals->frametime * MANNEDGUN_RESTORE_TURN_RATE) );
+ }
+ else
+ {
+ m_flGunPitch = flPitch + (gpGlobals->frametime * MANNEDGUN_RESTORE_TURN_RATE);
+ if ( m_flGunPitch >= 360 )
+ {
+ m_flGunPitch = 0;
+ }
+ }
+ }
+ else if ( m_flGunYaw )
+ {
+ if ( m_flGunYaw > 180 )
+ {
+ m_flGunYaw = m_flGunYaw + (gpGlobals->frametime * MANNEDGUN_RESTORE_TURN_RATE);
+ if ( m_flGunYaw >= 360 )
+ {
+ m_flGunYaw = 0;
+ }
+ }
+ else
+ {
+ m_flGunYaw = MAX( 0, m_flGunYaw - (gpGlobals->frametime * MANNEDGUN_RESTORE_TURN_RATE) );
+ }
+
+ }
+ else
+ {
+ // We're done
+ return;
+ }
+
+ // Keep thinking
+ SetContextThink( BaseMannedGunThink, gpGlobals->curtime + 0.1, OBJ_BASE_MANNEDGUN_THINK_CONTEXT );
+}
+
+#ifndef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Get and set the current driver.
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::SetPassenger( int nRole, CBasePlayer *pEnt )
+{
+ BaseClass::SetPassenger( nRole, pEnt );
+
+ // If we don't have a driver anymore, return to our original facing after a while
+ if ( !GetDriverPlayer() && (m_flGunPitch || m_flGunYaw) )
+ {
+ StopDesignating();
+ SetContextThink( BaseMannedGunThink, gpGlobals->curtime + MANNEDGUN_RESTORE_TIME, OBJ_BASE_MANNEDGUN_THINK_CONTEXT );
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Here's where we deal with weapons
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::OnItemPostFrame( CBaseTFPlayer *pDriver )
+{
+ // I can't do anything if I'm not active
+ if ( !ShouldBeActive() )
+ return;
+
+ if ( !IsReadyToDrive() )
+ return;
+
+ // If we don't have a laser designator yet, create one
+ if ( !m_hLaserDesignation )
+ {
+ m_hLaserDesignation = CEnvLaserDesignation::CreatePredicted( pDriver );
+ }
+
+ // Designating?
+ if (pDriver->m_nButtons & IN_ATTACK2)
+ {
+ UpdateDesignator();
+ return;
+ }
+
+ StopDesignating();
+
+ // Fire our base weapon?
+ if ( pDriver->m_nButtons & IN_ATTACK )
+ {
+ Fire();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::StopDesignating( void )
+{
+ // Remove our beam if we just stopped designating
+ if ( m_hBeam.Get() )
+ {
+ m_hBeam->Remove( );
+ }
+
+ if ( m_hLaserDesignation.Get() )
+ {
+ m_hLaserDesignation->SetActive( false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the designator position
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::UpdateDesignator( void )
+{
+ // Make the beam, if we don't have one yet
+ if ( !m_hBeam && GetDriverPlayer() )
+ {
+ m_hBeam = BEAM_CREATE_PREDICTABLE_PERSIST( "sprites/laserbeam.vmt", 5, GetDriverPlayer() );
+ if ( m_hBeam.Get() )
+ {
+ m_hBeam->PointEntInit( vec3_origin, this );
+ m_hBeam->SetEndAttachment( m_nBarrelAttachment );
+ m_hBeam->SetColor( 255, 32, 32 );
+ m_hBeam->SetBrightness( 255 );
+ m_hBeam->SetNoise( 0 );
+ m_hBeam->SetWidth( 0.5 );
+ m_hBeam->SetEndWidth( 0.5 );
+ }
+ }
+
+ // We have to flush the bone cache because it's possible that only the bone controllers
+ // have changed since the bonecache was generated, and bone controllers aren't checked.
+ InvalidateBoneCache();
+
+ QAngle vecAng;
+ Vector vecSrc, vecAim;
+ GetAttachment( m_nBarrelAttachment, vecSrc, vecAng );
+ AngleVectors( vecAng, &vecAim, 0, 0 );
+
+ // "Fire" the designator beam
+ Vector vecEnd = vecSrc + vecAim * obj_manned_gun_designator_range.GetFloat();
+ trace_t tr;
+ TFGameRules()->WeaponTraceLine(vecSrc, vecEnd, MASK_SHOT, this, DMG_PROBE, &tr);
+
+ if ( m_hLaserDesignation.Get() )
+ {
+ // Only update our designated target point if we hit something
+ if ( tr.fraction != 1.0 )
+ {
+ m_hLaserDesignation->SetActive( true );
+ m_hLaserDesignation->SetAbsOrigin( tr.endpos );
+ }
+ else
+ {
+ m_hLaserDesignation->SetActive( false );
+ }
+ }
+
+ // Update beam visual
+ if ( m_hBeam.Get() )
+ {
+ m_hBeam->SetStartPos( tr.endpos );
+ m_hBeam->RelinkBeam();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::SetupMove( CBasePlayer *pPlayer, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
+{
+ BaseClass::SetupMove( pPlayer, ucmd, pHelper, move );
+
+ CTFMoveData *pMoveData = (CTFMoveData*)move;
+ Assert( sizeof(MannedPlasmagunData_t) <= pMoveData->VehicleDataMaxSize() );
+
+ MannedPlasmagunData_t *pVehicleData = (MannedPlasmagunData_t*)pMoveData->VehicleData();
+ pVehicleData->m_pVehicle = this;
+ pVehicleData->m_flGunYaw = m_flGunYaw;
+ pVehicleData->m_flGunPitch = m_flGunPitch;
+ pVehicleData->m_flBarrelPitch = m_flBarrelPitch;
+ pVehicleData->m_nMoveStyle = m_nMoveStyle;
+ pVehicleData->m_flBarrelHeight = m_flBarrelHeight;
+ pVehicleData->m_nBarrelPivotAttachment = m_nBarrelPivotAttachment;
+ pVehicleData->m_nBarrelAttachment = m_nBarrelAttachment;
+ pVehicleData->m_nStandAttachment = m_nStandAttachment;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
+{
+ BaseClass::FinishMove( player, ucmd, move );
+ CTFMoveData *pMoveData = (CTFMoveData*)move;
+ Assert( sizeof(MannedPlasmagunData_t) <= pMoveData->VehicleDataMaxSize() );
+
+ MannedPlasmagunData_t *pVehicleData = (MannedPlasmagunData_t*)pMoveData->VehicleData();
+ m_flGunYaw = pVehicleData->m_flGunYaw;
+ m_flGunPitch = pVehicleData->m_flGunPitch;
+ m_flBarrelPitch = pVehicleData->m_flBarrelPitch;
+
+ // Set the bone state..
+ SetBoneController( 0, m_flGunYaw );
+ SetBoneController( 1, m_flGunPitch );
+
+ if ( m_nMoveStyle == MOVEMENT_STYLE_BARREL_PIVOT )
+ {
+ SetBoneController( 2, m_flBarrelPitch );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectBaseMannedGun::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove )
+{
+ m_Movement.ProcessMovement( pPlayer, pMove );
+
+ m_flGunPitch = AngleNormalize( m_flGunPitch );
+ m_flBarrelPitch = AngleNormalize( m_flBarrelPitch );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CObjectBaseMannedGun::GetGunYaw() const
+{
+ return m_flGunYaw;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CObjectBaseMannedGun::GetGunPitch() const
+{
+ return m_flGunPitch;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CObjectBaseMannedGun::ShouldUseThirdPersonVehicleView( void )
+{
+ if ( mannedgun_usethirdperson.GetInt() )
+ {
+ // We want to use third person if we're mounted on a vehicle.
+ return dynamic_cast< CBaseTFVehicle* >( GetMoveParent() ) != NULL;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_ObjectBaseMannedGun::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged(updateType);
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ // FIXME: Will this work with build animations models?
+
+ m_nBarrelAttachment = LookupAttachment( "barrel" );
+ m_nBarrelPivotAttachment = LookupAttachment( "barrelpivot" );
+ m_nStandAttachment = LookupAttachment( "vehicle_feet_passenger0" );
+
+ // Find the barrel height in its quiescent state...
+ Vector vBarrel;
+ QAngle vBarrelAngles;
+ GetAttachmentLocal(m_nBarrelAttachment, vBarrel, vBarrelAngles);
+ m_flBarrelHeight = vBarrel.z;
+
+ // HACK HACK: This should be read from a .txt file at some point!!!!
+ CHudTexture newTexture;
+ Q_strncpy( newTexture.szTextureFile, "sprites/crosshairs", sizeof( newTexture.szTextureFile ) );
+
+ newTexture.rc.left = 0;
+ newTexture.rc.top = 48;
+ newTexture.rc.right = newTexture.rc.left + 24;
+ newTexture.rc.bottom = newTexture.rc.top + 24;
+ iconCrosshair = gHUD.AddUnsearchableHudIconToList( newTexture );
+ }
+ else
+ {
+ // Set the bone state..
+ SetBoneController( 0, m_flGunYaw );
+ SetBoneController( 1, m_flGunPitch );
+
+ if ( m_nMoveStyle == MOVEMENT_STYLE_BARREL_PIVOT )
+ {
+ SetBoneController( 2, m_flBarrelPitch );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Clamps the view angles while manning the gun
+//-----------------------------------------------------------------------------
+void C_ObjectBaseMannedGun::UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd )
+{
+#if 0
+ // Confine the view to the appropriate yaw range...
+ float flAngleDiff = AngleDiff( pCmd->viewangles[YAW], flCenterYaw );
+
+ // Here, we must clamp to the cone...
+ if (flAngleDiff < m_Movement.GetMinYaw())
+ pCmd->viewangles[YAW] = anglemod(flCenterYaw + m_Movement.GetMinYaw());
+ else if (flAngleDiff > m_Movement.GetMaxYaw())
+ pCmd->viewangles[YAW] = anglemod(flCenterYaw + m_Movement.GetMaxYaw());
+#endif
+
+ // Prevent too much downward looking
+ if ( pCmd->viewangles[PITCH] > m_Movement.GetMaxPitch())
+ {
+ pCmd->viewangles[PITCH] = m_Movement.GetMaxPitch();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Orients the gun correctly
+//-----------------------------------------------------------------------------
+void C_ObjectBaseMannedGun::GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS], float dadt)
+{
+ // turret angle values:
+ // 0 = front, 90 = left, 180 = back, 270 = right
+ studiohdr_t *pModel = modelinfo->GetStudiomodel( GetModel() );
+ Studio_SetController(pModel, 0, m_flGunYaw, controllers[0]);
+ Studio_SetController(pModel, 1, m_flGunPitch, controllers[1]);
+
+ if ( m_nMoveStyle == MOVEMENT_STYLE_BARREL_PIVOT )
+ {
+ Studio_SetController(pModel, 2, m_flBarrelPitch, controllers[2]);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the angles that a player in the specified role should be using for visuals
+//-----------------------------------------------------------------------------
+QAngle C_ObjectBaseMannedGun::GetPassengerAngles( QAngle angCurrent, int nRole )
+{
+ // Stomp the current angle's pitch with our rotation
+ QAngle vecNewAngles = angCurrent;
+ angCurrent[PITCH] = m_flGunPitch;
+
+ return angCurrent;
+}
+
+//-----------------------------------------------------------------------------
+// Renders hud elements
+//-----------------------------------------------------------------------------
+void C_ObjectBaseMannedGun::DrawHudElements( void )
+{
+ GetHudAmmo()->SetPrimaryAmmo( m_nAmmoType, m_nAmmoCount );
+ GetHudAmmo()->SetSecondaryAmmo( -1, -1 );
+
+ // Let the plasma gun operator see a crosshair
+ DrawCrosshair();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the weapon's crosshair
+//-----------------------------------------------------------------------------
+void C_ObjectBaseMannedGun::DrawCrosshair()
+{
+ C_BasePlayer *player = C_BasePlayer::GetLocalPlayer();
+ if ( !player )
+ return;
+
+ CHudCrosshair *crosshair = GET_HUDELEMENT( CHudCrosshair );
+ if ( !crosshair )
+ return;
+
+ if ( iconCrosshair )
+ {
+ crosshair->SetCrosshair( iconCrosshair, gHUD.m_clrNormal );
+ }
+ else
+ {
+ static wrect_t nullrc;
+ crosshair->SetCrosshair( 0, Color( 255, 255, 255, 255 ) );
+ }
+}
+
+#endif
diff --git a/game/shared/tf2/tf_obj_base_manned_gun.h b/game/shared/tf2/tf_obj_base_manned_gun.h
new file mode 100644
index 0000000..d2b8ef3
--- /dev/null
+++ b/game/shared/tf2/tf_obj_base_manned_gun.h
@@ -0,0 +1,169 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A stationary gun that players can man
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_OBJ_BASE_MANNED_GUN_H
+#define TF_OBJ_BASE_MANNED_GUN_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetfvehicle.h"
+#include "tf_obj_manned_plasmagun_shared.h"
+#include "env_laserdesignation.h"
+#include "beam_shared.h"
+
+class CMoveData;
+
+#if defined( CLIENT_DLL )
+
+#define CObjectBaseMannedGun C_ObjectBaseMannedGun
+#define CBaseTFVehicle C_BaseTFVehicle
+
+#endif
+
+// ------------------------------------------------------------------------ //
+// A stationary gun that players can man that's built by the player
+// ------------------------------------------------------------------------ //
+class CObjectBaseMannedGun : public CBaseTFVehicle
+{
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+ DECLARE_CLASS( CObjectBaseMannedGun, CBaseTFVehicle );
+
+ CObjectBaseMannedGun();
+
+ virtual void Spawn();
+ virtual void Precache();
+ virtual void UpdateOnRemove( void );
+ virtual void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName );
+ virtual bool CanTakeEMPDamage( void ) { return true; }
+ virtual void OnGoInactive( void );
+
+ // Vehicle overrides
+#ifndef CLIENT_DLL
+ virtual void SetPassenger( int nRole, CBasePlayer *pEnt );
+#endif
+ virtual bool IsPassengerVisible( int nRole = VEHICLE_DRIVER ) { return true; }
+
+ // Returns the eye position
+ virtual void GetVehicleViewPosition( int nRole, Vector *pOrigin, QAngle *pAngles, float *pFOV = NULL );
+
+ // Manned plasma passengers aren't damagable
+ //virtual bool IsPassengerDamagable( int nRole = VEHICLE_DRIVER ) { return false; }
+
+ virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove );
+ virtual void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move );
+ virtual void FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move );
+
+ virtual bool ShouldAttachToParent( void ) { return true; }
+
+ virtual bool MustNotBeBuiltInConstructionYard( void ) const { return true; }
+
+ virtual bool ShouldUseThirdPersonVehicleView( void );
+
+ virtual void BaseMannedGunThink( void );
+
+ float GetGunYaw() const;
+ float GetGunPitch() const;
+
+ // Buff
+ bool CanBeHookedToBuffStation( void );
+
+#if defined ( CLIENT_DLL )
+// IClientVehicle overrides.
+public:
+ virtual void DrawHudElements( void );
+ virtual void UpdateViewAngles( C_BasePlayer *pLocalPlayer, CUserCmd *pCmd );
+
+ virtual QAngle GetPassengerAngles( QAngle angCurrent, int nRole );
+
+// C_BaseEntity overrides.
+public:
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS], float dadt);
+
+private:
+ void DrawCrosshair( void );
+
+#endif
+
+protected:
+ // Sets up various attachment points once the model is selected
+ // Derived classes should call this from within their SetTeamModel call
+ void OnModelSelected();
+
+ // Can we get into the vehicle?
+ virtual bool CanGetInVehicle( CBaseTFPlayer *pPlayer );
+
+ // Here's where we deal with weapons
+ virtual void OnItemPostFrame( CBaseTFPlayer *pPassenger );
+
+ // Fire the weapon
+ virtual void Fire( void ) {}
+
+ void StopDesignating( void );
+ void UpdateDesignator( void );
+
+ virtual void SetupAttachedVersion( void );
+ virtual void SetupUnattachedVersion( void );
+
+ // Sets the movement style
+ void SetMovementStyle( MovementStyle_t style );
+
+ // Calculate the max range of this gun
+ void CalculateMaxRange( float flDefensiveRange, float flOffensiveRange );
+
+protected:
+ // Movement...
+ CObjectMannedPlasmagunMovement m_Movement;
+
+ float m_flMaxRange;
+
+ // attachment points
+ int m_nBarrelAttachment;
+ int m_nBarrelPivotAttachment;
+ int m_nStandAttachment;
+ int m_nEyesAttachment;
+
+ // Movement style
+ CNetworkVar( MovementStyle_t, m_nMoveStyle );
+
+ // Barrel height...
+ float m_flBarrelHeight;
+
+ CNetworkVar( int, m_nAmmoType );
+ CNetworkVar( int, m_nAmmoCount );
+ CNetworkVar( float, m_flGunYaw ); // 0 = front, 90 = left, 180 = back, 270 = right
+ CNetworkVar( float, m_flGunPitch ); // 0 = forward, -90 = pointing down, 90 = pointing up..
+ CNetworkVar( float, m_flBarrelPitch );
+
+ float m_flReturnToInitialTime;
+
+ // Laser designation
+ CNetworkHandle( CBeam, m_hBeam );
+ CNetworkHandle( CEnvLaserDesignation, m_hLaserDesignation );
+
+#if defined( CLIENT_DLL )
+ CHudTexture *iconCrosshair;
+
+private:
+ CObjectBaseMannedGun( const CObjectBaseMannedGun & ); // not defined, not accessible
+#endif
+};
+
+
+//-----------------------------------------------------------------------------
+// Inline methods
+//-----------------------------------------------------------------------------
+inline bool CObjectBaseMannedGun::CanBeHookedToBuffStation( void )
+{
+ return true;
+}
+
+#endif // TF_OBJ_BASE_MANNED_GUN_H
diff --git a/game/shared/tf2/tf_obj_basedrivergun_shared.cpp b/game/shared/tf2/tf_obj_basedrivergun_shared.cpp
new file mode 100644
index 0000000..e0dabca
--- /dev/null
+++ b/game/shared/tf2/tf_obj_basedrivergun_shared.cpp
@@ -0,0 +1,92 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Base class for object upgrading objects
+//
+//=============================================================================//
+#include "cbase.h"
+#include "baseobject_shared.h"
+#include "tf_obj_basedrivergun_shared.h"
+#include "basetfvehicle.h"
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseObjectDriverGun, DT_BaseObjectDriverGun )
+
+BEGIN_NETWORK_TABLE( CBaseObjectDriverGun, DT_BaseObjectDriverGun )
+#if !defined( CLIENT_DLL )
+ SendPropVector( SENDINFO(m_vecGunAngles), -1, SPROP_COORD ),
+#else
+ RecvPropVector( RECVINFO(m_vecGunAngles) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CBaseObjectDriverGun )
+ DEFINE_PRED_FIELD_TOL( m_vecGunAngles, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 1.0f ),
+END_PREDICTION_DATA()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseObjectDriverGun::CBaseObjectDriverGun()
+{
+ m_vecGunAngles = QAngle(0,0,0);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObjectDriverGun::Spawn()
+{
+ BaseClass::Spawn();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObjectDriverGun::FinishedBuilding( void )
+{
+#if !defined( CLIENT_DLL )
+ BaseClass::FinishedBuilding();
+
+ CBaseTFVehicle *pVehicle = dynamic_cast<CBaseTFVehicle*>(GetParentObject());
+ Assert( pVehicle );
+
+ pVehicle->SetDriverGun( this );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObjectDriverGun::SetTargetAngles( const QAngle &vecAngles )
+{
+ m_vecGunAngles = vecAngles;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const QAngle &CBaseObjectDriverGun::GetCurrentAngles( void )
+{
+ return m_vecGunAngles.Get();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Vector CBaseObjectDriverGun::GetFireOrigin( void )
+{
+ return GetAbsOrigin();
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseObjectDriverGun::ShouldPredict( void )
+{
+ CBaseTFVehicle *pVehicle = dynamic_cast<CBaseTFVehicle*>(GetParentObject());
+ if ( pVehicle && pVehicle->GetDriverPlayer() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+}
+#endif \ No newline at end of file
diff --git a/game/shared/tf2/tf_obj_basedrivergun_shared.h b/game/shared/tf2/tf_obj_basedrivergun_shared.h
new file mode 100644
index 0000000..3d79cd1
--- /dev/null
+++ b/game/shared/tf2/tf_obj_basedrivergun_shared.h
@@ -0,0 +1,64 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Base class for object upgrading objects
+//
+//=============================================================================//
+
+#ifndef TF_OBJ_BASEDRIVERGUN_H
+#define TF_OBJ_BASEDRIVERGUN_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#if defined( CLIENT_DLL )
+#define CBaseObjectDriverGun C_BaseObjectDriverGun
+#endif
+
+#include "tf_obj_baseupgrade_shared.h"
+
+// ------------------------------------------------------------------------
+// Base class for objects that, when built on a vehicle, become under control of the driver
+// ------------------------------------------------------------------------
+class CBaseObjectDriverGun : public CBaseObjectUpgrade
+{
+DECLARE_CLASS( CBaseObjectDriverGun, CBaseObjectUpgrade );
+
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CBaseObjectDriverGun();
+
+ virtual void Spawn( void );
+ virtual void FinishedBuilding( void );
+
+ // Firing
+ virtual bool CanFireNow( void ) { return false; }
+ virtual void Fire( CBaseTFPlayer *pDriver ) { return; }
+
+ // Turning
+ virtual void SetTargetAngles( const QAngle &vecAngles );
+ virtual const QAngle &GetCurrentAngles( void );
+ virtual Vector GetFireOrigin( void );
+
+ // HUD
+ virtual void DrawHudElements( void ) { return; }
+
+#ifdef CLIENT_DLL
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+ virtual bool ShouldPredict( void );
+#endif
+
+protected:
+ CNetworkQAngle( m_vecGunAngles );
+
+private:
+ CBaseObjectDriverGun( const CBaseObjectDriverGun & ); // not defined, not accessible
+};
+
+#endif // TF_OBJ_BASEDRIVERGUN_H
diff --git a/game/shared/tf2/tf_obj_baseupgrade_shared.cpp b/game/shared/tf2/tf_obj_baseupgrade_shared.cpp
new file mode 100644
index 0000000..9f0f30f
--- /dev/null
+++ b/game/shared/tf2/tf_obj_baseupgrade_shared.cpp
@@ -0,0 +1,58 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Base class for object upgrading objects
+//
+//=============================================================================//
+#include "cbase.h"
+#include "baseobject_shared.h"
+#include "tf_obj_baseupgrade_shared.h"
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseObjectUpgrade, DT_BaseObjectUpgrade )
+
+BEGIN_NETWORK_TABLE( CBaseObjectUpgrade, DT_BaseObjectUpgrade )
+END_NETWORK_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseObjectUpgrade::CBaseObjectUpgrade()
+{
+#if !defined( CLIENT_DLL )
+ UseClientSideAnimation();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseObjectUpgrade::Spawn()
+{
+ BaseClass::Spawn();
+
+#if !defined( CLIENT_DLL )
+ m_fObjectFlags |= OF_DONT_PREVENT_BUILD_NEAR_OBJ | OF_ALLOW_REPEAT_PLACEMENT |
+ OF_DOESNT_NEED_POWER | OF_MUST_BE_BUILT_ON_ATTACHMENT;
+
+ // Prevent anyone shooting / emping / buffing me
+ SetSolid( SOLID_NONE );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Prevent Team Damage
+//-----------------------------------------------------------------------------
+int CBaseObjectUpgrade::OnTakeDamage( const CTakeDamageInfo &info )
+{
+#if !defined( CLIENT_DLL )
+ // Check teams
+ if ( info.GetDamageType() & DMG_BLAST )
+ {
+ return 0;
+ }
+
+ return BaseClass::OnTakeDamage( info );
+#else
+ return 0;
+#endif
+} \ No newline at end of file
diff --git a/game/shared/tf2/tf_obj_baseupgrade_shared.h b/game/shared/tf2/tf_obj_baseupgrade_shared.h
new file mode 100644
index 0000000..cccbc63
--- /dev/null
+++ b/game/shared/tf2/tf_obj_baseupgrade_shared.h
@@ -0,0 +1,41 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Base class for object upgrading objects
+//
+//=============================================================================//
+
+#ifndef TF_OBJ_BASEUPGRADE_H
+#define TF_OBJ_BASEUPGRADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "baseobject_shared.h"
+#include "takedamageinfo.h"
+
+#if defined( CLIENT_DLL )
+#define CBaseObjectUpgrade C_BaseObjectUpgrade
+#endif
+
+// ------------------------------------------------------------------------ //
+// Base class for object upgrading objects
+// ------------------------------------------------------------------------ //
+class CBaseObjectUpgrade : public CBaseObject
+{
+DECLARE_CLASS( CBaseObjectUpgrade, CBaseObject );
+
+public:
+ DECLARE_NETWORKCLASS();
+
+ CBaseObjectUpgrade();
+
+ virtual void Spawn( void );
+ virtual bool IsAnUpgrade( void ) { return true; }
+ virtual int OnTakeDamage( const CTakeDamageInfo &info );
+
+private:
+ CBaseObjectUpgrade( const CBaseObjectUpgrade & ); // not defined, not accessible
+};
+
+#endif // TF_OBJ_BASEUPGRADE_H
diff --git a/game/shared/tf2/tf_obj_driver_machinegun_shared.cpp b/game/shared/tf2/tf_obj_driver_machinegun_shared.cpp
new file mode 100644
index 0000000..f914978
--- /dev/null
+++ b/game/shared/tf2/tf_obj_driver_machinegun_shared.cpp
@@ -0,0 +1,219 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Vehicle mounted machinegun that the driver controls
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "baseobject_shared.h"
+#include "tf_obj_driver_machinegun_shared.h"
+#include "engine/IEngineSound.h"
+#include "ammodef.h"
+#include "tf_gamerules.h"
+
+#if defined( CLIENT_DLL )
+#include "hud_ammo.h"
+#else
+#endif
+
+// ------------------------------------------------------------------------ //
+#define DRIVER_MACHINEGUN_MINS Vector(-10, -10, 0)
+#define DRIVER_MACHINEGUN_MAXS Vector( 10, 10, 10)
+#define DRIVER_MACHINEGUN_MODEL "models/objects/obj_manned_plasmagun.mdl"
+
+IMPLEMENT_NETWORKCLASS_ALIASED( ObjectDriverMachinegun, DT_ObjectDriverMachinegun )
+
+BEGIN_NETWORK_TABLE( CObjectDriverMachinegun, DT_ObjectDriverMachinegun )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO(m_nAmmoCount), 7, SPROP_UNSIGNED ),
+#else
+ RecvPropInt( RECVINFO(m_nAmmoCount) ),
+#endif
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS(obj_driver_machinegun, CObjectDriverMachinegun);
+PRECACHE_REGISTER(obj_driver_machinegun);
+
+BEGIN_PREDICTION_DATA( CObjectDriverMachinegun )
+ DEFINE_PRED_FIELD( m_nAmmoCount, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+
+ConVar obj_driver_machinegun_health( "obj_driver_machinegun_health","100", FCVAR_REPLICATED, "Driver's mounted machinegun health" );
+ConVar obj_driver_machinegun_range( "obj_driver_machinegun_range","1048", FCVAR_REPLICATED, "Driver's mounted machinegun range" );
+ConVar obj_driver_machinegun_damage( "obj_driver_machinegun_damage","10", FCVAR_REPLICATED, "Driver's mounted machinegun damage" );
+ConVar obj_driver_machinegun_max_ammo( "obj_driver_machinegun_max_ammo","30", FCVAR_REPLICATED, "Driver's mounted machinegun ammo" );
+ConVar obj_driver_machinegun_ammo_recharge_rate( "obj_driver_machinegun_ammo_recharge_rate","0.5", FCVAR_REPLICATED, "Driver's mounted machinegun ammo recharge rate" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CObjectDriverMachinegun::CObjectDriverMachinegun()
+{
+ SetPredictionEligible( true );
+
+#if !defined( CLIENT_DLL )
+ m_iHealth = obj_driver_machinegun_health.GetInt();
+#endif
+
+ m_flNextAttack = 0;
+ m_nMaxAmmoCount = m_nAmmoCount = obj_driver_machinegun_max_ammo.GetInt();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectDriverMachinegun::Spawn()
+{
+ Precache();
+ SetModel( DRIVER_MACHINEGUN_MODEL );
+
+#if !defined( CLIENT_DLL )
+ SetBodygroup( 1, true );
+
+ UTIL_SetSize(this, DRIVER_MACHINEGUN_MINS, DRIVER_MACHINEGUN_MAXS);
+ m_takedamage = DAMAGE_YES;
+
+ SetType( OBJ_DRIVER_MACHINEGUN );
+ m_fObjectFlags |= OF_SUPPRESS_NOTIFY_UNDER_ATTACK | OF_DONT_AUTO_REPAIR;
+
+ SetThink( RechargeThink );
+#endif
+
+ m_nBarrelAttachment = LookupAttachment( "barrel" );
+ m_nAmmoType = GetAmmoDef()->Index( "Bullets" );
+
+ BaseClass::Spawn();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectDriverMachinegun::Precache()
+{
+ PrecacheModel( DRIVER_MACHINEGUN_MODEL );
+
+ PrecacheScriptSound( "DriverMachinegun.Fire" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CObjectDriverMachinegun::CanFireNow( void )
+{
+ if ( !m_nAmmoCount )
+ return false;
+
+ return ( m_flNextAttack < gpGlobals->curtime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectDriverMachinegun::Fire( CBaseTFPlayer *pDriver )
+{
+ // We have to flush the bone cache because it's possible that only the bone controllers
+ // have changed since the bonecache was generated, and bone controllers aren't checked.
+ InvalidateBoneCache();
+
+ QAngle vecAng;
+ Vector vecSrc, vecAim;
+ GetAttachment( m_nBarrelAttachment, vecSrc, vecAng );
+ AngleVectors( vecAng, &vecAim, 0, 0 );
+
+ static Vector spread = VECTOR_CONE_5DEGREES;
+ TFGameRules()->FireBullets( CTakeDamageInfo( this, pDriver, obj_driver_machinegun_damage.GetFloat(), DMG_BULLET ),
+ 1, vecSrc, vecAim, spread, obj_driver_machinegun_range.GetFloat(), m_nAmmoType, 4, entindex(), m_nBarrelAttachment );
+
+#if !defined (CLIENT_DLL)
+ SetActivity( ACT_VM_PRIMARYATTACK );
+#else
+ int sequence = SelectWeightedSequence( ACT_VM_PRIMARYATTACK );
+ if ( sequence != ACTIVITY_NOT_AVAILABLE )
+ {
+ ResetSequence( sequence );
+ SetCycle( 0 );
+ m_bClientSideFrameReset = !m_bClientSideFrameReset;
+ }
+#endif
+ DoMuzzleFlash();
+
+ CPASAttenuationFilter filter( this );
+ filter.UsePredictionRules();
+ EmitSound( filter, entindex(), "DriverMachinegun.Fire" );
+
+ m_nAmmoCount -= 1;
+
+#if !defined (CLIENT_DLL)
+ SetNextThink( gpGlobals->curtime + obj_driver_machinegun_ammo_recharge_rate.GetFloat() );
+#endif
+
+ // If I'm EMPed, slow the firing rate down
+ m_flNextAttack = gpGlobals->curtime + ( HasPowerup(POWERUP_EMP) ? 0.3f : 0.15f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectDriverMachinegun::SetTargetAngles( const QAngle &vecAngles )
+{
+ BaseClass::SetTargetAngles( vecAngles );
+
+#if !defined( CLIENT_DLL )
+ SetBoneController( 0, vecAngles[YAW] );
+ SetBoneController( 1, vecAngles[PITCH] );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Vector CObjectDriverMachinegun::GetFireOrigin( void )
+{
+ Vector vecOrigin;
+ QAngle dummy;
+ GetAttachment( m_nBarrelAttachment, vecOrigin, dummy );
+ return vecOrigin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Rcharge my ammo count
+//-----------------------------------------------------------------------------
+void CObjectDriverMachinegun::RechargeThink( void )
+{
+#if !defined( CLIENT_DLL )
+ SetNextThink( gpGlobals->curtime + obj_driver_machinegun_ammo_recharge_rate.GetFloat() );
+
+ // I can't do anything if I'm not active
+ if ( !ShouldBeActive() )
+ return;
+
+ if (m_nAmmoCount < m_nMaxAmmoCount)
+ {
+ m_nAmmoCount += 1;
+ }
+#endif
+}
+
+// Client code only
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectDriverMachinegun::GetBoneControllers( float controllers[MAXSTUDIOBONECTRLS] )
+{
+ BaseClass::GetBoneControllers( controllers );
+
+ controllers[0] = anglemod( m_vecGunAngles[YAW] ) / 360.0;
+ controllers[1] = anglemod( m_vecGunAngles[PITCH] ) / 360.0;
+}
+
+//-----------------------------------------------------------------------------
+// Renders hud elements
+//-----------------------------------------------------------------------------
+void CObjectDriverMachinegun::DrawHudElements( void )
+{
+ GetHudAmmo()->SetPrimaryAmmo( m_nAmmoType, m_nAmmoCount );
+ GetHudAmmo()->SetSecondaryAmmo( -1, -1 );
+}
+#endif
diff --git a/game/shared/tf2/tf_obj_driver_machinegun_shared.h b/game/shared/tf2/tf_obj_driver_machinegun_shared.h
new file mode 100644
index 0000000..436d421
--- /dev/null
+++ b/game/shared/tf2/tf_obj_driver_machinegun_shared.h
@@ -0,0 +1,62 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Vehicle mounted machinegun that the driver controls
+//
+//=============================================================================//
+
+#ifndef TF_OBJ_DRIVER_MACHINEGUN_H
+#define TF_OBJ_DRIVER_MACHINEGUN_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_obj_basedrivergun_shared.h"
+
+#if defined( CLIENT_DLL )
+#define CObjectDriverMachinegun C_ObjectDriverMachinegun
+#endif
+
+// ------------------------------------------------------------------------ //
+// Mounted machinegun
+// ------------------------------------------------------------------------ //
+class CObjectDriverMachinegun : public CBaseObjectDriverGun
+{
+DECLARE_CLASS( CObjectDriverMachinegun, CBaseObjectDriverGun );
+
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CObjectDriverMachinegun();
+
+ virtual void Spawn();
+ virtual void Precache();
+
+ // Firing
+ virtual bool CanFireNow( void );
+ virtual void Fire( CBaseTFPlayer *pDriver );
+
+ // Turning
+ virtual Vector GetFireOrigin( void );
+ virtual void SetTargetAngles( const QAngle &vecAngles );
+
+#if defined( CLIENT_DLL )
+ void GetBoneControllers( float controllers[MAXSTUDIOBONECTRLS] );
+ virtual void DrawHudElements( void );
+#endif
+
+ // Ammo
+ void RechargeThink( void );
+
+private:
+ float m_flNextAttack;
+ int m_nBarrelAttachment;
+ int m_nAmmoType;
+ CNetworkVar( int, m_nAmmoCount );
+ int m_nMaxAmmoCount;
+
+private:
+ CObjectDriverMachinegun( const CObjectDriverMachinegun & ); // not defined, not accessible
+};
+
+#endif // TF_OBJ_DRIVER_MACHINEGUN_H
diff --git a/game/shared/tf2/tf_obj_manned_plasmagun.cpp b/game/shared/tf2/tf_obj_manned_plasmagun.cpp
new file mode 100644
index 0000000..0743bfc
--- /dev/null
+++ b/game/shared/tf2/tf_obj_manned_plasmagun.cpp
@@ -0,0 +1,304 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A stationary gun that players can man
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "tf_obj_manned_plasmagun.h"
+#include "ammodef.h"
+#include "plasmaprojectile.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "tf_gamerules.h"
+
+#define MANNED_PLASMAGUN_MINS Vector(-20, -20, 0)
+#define MANNED_PLASMAGUN_MAXS Vector( 20, 20, 55)
+#define MANNED_PLASMAGUN_ALIEN_MODEL "models/objects/obj_manned_plasmagun.mdl"
+#define MANNED_PLASMAGUN_HUMAN_MODEL "models/objects/human_obj_manned_plasmagun.mdl"
+
+#define MANNED_PLASMAGUN_RECHARGE_TIME 0.2
+#define MANNED_PLASMAGUN_IDLE_RECHARGE_TIME 0.1
+
+#define MANNED_PLASMAGUN_IDLE_TIME 2.0
+
+#if !defined( CLIENT_DLL )
+BEGIN_DATADESC( CObjectMannedPlasmagun )
+
+ DEFINE_THINKFUNC( RechargeThink ),
+
+END_DATADESC()
+#endif
+
+IMPLEMENT_NETWORKCLASS_ALIASED( ObjectMannedPlasmagun, DT_ObjectMannedPlasmagun )
+
+BEGIN_NETWORK_TABLE( CObjectMannedPlasmagun, DT_ObjectMannedPlasmagun )
+#if !defined( CLIENT_DLL )
+ SendPropTime( SENDINFO( m_flNextIdleTime ) ),
+ SendPropInt( SENDINFO( m_bFiringLeft ), 1, SPROP_UNSIGNED ),
+
+ SendPropInt( SENDINFO( m_nNextThinkTick ) ),
+#else
+ RecvPropTime( RECVINFO( m_flNextIdleTime ) ),
+ RecvPropInt( RECVINFO( m_bFiringLeft ) ),
+
+ RecvPropInt ( RECVINFO( m_nNextThinkTick ) ),
+
+#endif
+END_NETWORK_TABLE()
+
+
+BEGIN_PREDICTION_DATA( CObjectMannedPlasmagun )
+ DEFINE_PRED_FIELD_TOL( m_flNextIdleTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+ DEFINE_PRED_FIELD( m_bFiringLeft, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_FIELD( m_nNextThinkTick, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+
+// DEFINE_FIELD( m_nRightBarrelAttachment, FIELD_INTEGER ),
+ DEFINE_FIELD( m_nMaxAmmoCount, FIELD_INTEGER ),
+
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS(obj_manned_plasmagun, CObjectMannedPlasmagun);
+PRECACHE_REGISTER(obj_manned_plasmagun);
+
+// CVars
+ConVar obj_manned_plasmagun_health( "obj_manned_plasmagun_health","100", FCVAR_REPLICATED, "Manned Plasmagun health" );
+ConVar obj_manned_plasmagun_range_def( "obj_manned_plasmagun_range_def","1000", FCVAR_REPLICATED, "Defensive Manned Plasmagun range" );
+ConVar obj_manned_plasmagun_range_off( "obj_manned_plasmagun_range_off","1000", FCVAR_REPLICATED, "Offensive Manned Plasmagun range" );
+ConVar obj_manned_plasmagun_damage( "obj_manned_plasmagun_damage","20", FCVAR_REPLICATED, "Manned Plasmagun damage" );
+ConVar obj_manned_plasmagun_radius( "obj_manned_plasmagun_radius","128", FCVAR_REPLICATED, "Manned Plasmagun explosive radius" );
+ConVar obj_manned_plasmagun_clip( "obj_manned_plasmagun_clip","35", FCVAR_REPLICATED, "Manned Plasmagun's clip size" );
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CObjectMannedPlasmagun::CObjectMannedPlasmagun()
+{
+ m_bFiringLeft = true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectMannedPlasmagun::Precache()
+{
+ BaseClass::Precache();
+ PrecacheModel( MANNED_PLASMAGUN_ALIEN_MODEL );
+ PrecacheModel( MANNED_PLASMAGUN_HUMAN_MODEL );
+
+ PrecacheScriptSound( "ObjectMannedPlasmagun.Fire" );
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectMannedPlasmagun::SetupTeamModel( void )
+{
+ // FIXME: When adding in build animations here, make sure C_ObjectBaseMannedGun::OnDataChanged
+ // does the right thing on the client!!
+ if ( GetTeamNumber() == TEAM_HUMANS )
+ {
+ SetMovementStyle( MOVEMENT_STYLE_BARREL_PIVOT );
+ SetModel( MANNED_PLASMAGUN_HUMAN_MODEL );
+ }
+ else
+ {
+ SetMovementStyle( MOVEMENT_STYLE_STANDARD );
+ SetModel( MANNED_PLASMAGUN_ALIEN_MODEL );
+ }
+
+ // Call this to get all the attachment points happy
+ OnModelSelected();
+
+ // Get our extra barrel
+ m_nRightBarrelAttachment = LookupAttachment( "barrelR" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CObjectMannedPlasmagun::Spawn()
+{
+ Precache();
+
+ SetSolid( SOLID_BBOX );
+
+ SetSize( MANNED_PLASMAGUN_MINS, MANNED_PLASMAGUN_MAXS );
+ SetHealth( obj_manned_plasmagun_health.GetInt() );
+
+ SetNextThink( gpGlobals->curtime + MANNED_PLASMAGUN_IDLE_RECHARGE_TIME );
+
+ SetType( OBJ_MANNED_PLASMAGUN );
+
+ m_nAmmoCount = m_nMaxAmmoCount = obj_manned_plasmagun_clip.GetInt();
+ m_flNextAttack = gpGlobals->curtime;
+ m_nAmmoType = GetAmmoDef()->Index( "RechargeEnergy" );
+ SetThink( RechargeThink );
+
+ BaseClass::Spawn();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finished the build
+//-----------------------------------------------------------------------------
+void CObjectMannedPlasmagun::FinishedBuilding( void )
+{
+ BaseClass::FinishedBuilding();
+
+ CalculateMaxRange( obj_manned_plasmagun_range_def.GetFloat(), obj_manned_plasmagun_range_off.GetFloat() );
+}
+
+//-----------------------------------------------------------------------------
+// Recharge think...
+//-----------------------------------------------------------------------------
+void CObjectMannedPlasmagun::RechargeThink( )
+{
+ // Prevent manned guns from deteriorating
+ ResetDeteriorationTime();
+
+ float flNextRechargeTime = MANNED_PLASMAGUN_RECHARGE_TIME;
+ /*
+ ROBIN: Remove idle recharging for now
+
+ if (gpGlobals->curtime >= m_flNextIdleTime)
+ flNextRechargeTime = MANNED_PLASMAGUN_IDLE_RECHARGE_TIME;
+ else
+ flNextRechargeTime = MANNED_PLASMAGUN_RECHARGE_TIME;
+ */
+
+ // If I'm EMPed, slow the recharge rate down
+ if ( HasPowerup(POWERUP_EMP) )
+ {
+ flNextRechargeTime *= 1.5;
+ }
+ SetNextThink( gpGlobals->curtime + flNextRechargeTime );
+
+ // I can't do anything if I'm not active
+ if ( !ShouldBeActive() )
+ return;
+
+ if (m_nAmmoCount < m_nMaxAmmoCount)
+ {
+ ++m_nAmmoCount;
+ }
+ else
+ {
+ // No need to think when it's full
+ SetNextThink( gpGlobals->curtime + 5.0f );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Plasma sentrygun's fire
+//-----------------------------------------------------------------------------
+void CObjectMannedPlasmagun::Fire( )
+{
+ if (m_flNextAttack > gpGlobals->curtime)
+ return;
+
+ // Because the plasma sentrygun always thinks it has ammo (see below)
+ // we might not have ammo here, in which case we should just abort.
+ if ( !m_nAmmoCount )
+ return;
+
+ // Make sure we think soon enough in case of firing...
+ float flNextRecharge = gpGlobals->curtime + (HasPowerup(POWERUP_EMP) ? MANNED_PLASMAGUN_RECHARGE_TIME * 1.5 : MANNED_PLASMAGUN_RECHARGE_TIME);
+ SetNextThink( gpGlobals->curtime + flNextRecharge );
+
+ // We have to flush the bone cache because it's possible that only the bone controllers
+ // have changed since the bonecache was generated, and bone controllers aren't checked.
+ InvalidateBoneCache();
+
+ QAngle vecAng;
+ Vector vecSrc, vecAim;
+
+ // Alternate barrels when firing
+ if ( m_bFiringLeft )
+ {
+ // Aliens permanently fire left barrel because they have no right
+ if ( GetTeamNumber() == TEAM_HUMANS )
+ {
+ m_bFiringLeft = false;
+ }
+ GetAttachment( m_nBarrelAttachment, vecSrc, vecAng );
+ SetActivity( ACT_VM_PRIMARYATTACK );
+ }
+ else
+ {
+ m_bFiringLeft = true;
+ GetAttachment( m_nRightBarrelAttachment, vecSrc, vecAng );
+ SetActivity( ACT_VM_SECONDARYATTACK );
+ }
+
+ // Get the distance to the target
+ AngleVectors( vecAng, &vecAim, 0, 0 );
+
+ int damageType = GetAmmoDef()->DamageType( m_nAmmoType );
+ CBasePlasmaProjectile *pPlasma = CBasePlasmaProjectile::CreatePredicted( vecSrc, vecAim, Vector( 0, 0, 0 ), damageType, GetDriverPlayer() );
+ if ( pPlasma )
+ {
+ pPlasma->SetDamage( obj_manned_plasmagun_damage.GetFloat() );
+ pPlasma->m_hOwner = GetDriverPlayer();
+ //pPlasma->SetOwnerEntity( this );
+ pPlasma->SetMaxRange( m_flMaxRange );
+ if ( obj_manned_plasmagun_radius.GetFloat() )
+ {
+ pPlasma->SetExplosive( obj_manned_plasmagun_radius.GetFloat() );
+ }
+ }
+
+ CSoundParameters params;
+ if ( GetParametersForSound( "ObjectMannedPlasmagun.Fire", params, NULL ) )
+ {
+ CPASAttenuationFilter filter( this, params.soundlevel );
+ if ( IsPredicted() )
+ {
+ filter.UsePredictionRules();
+ }
+ EmitSound( filter, entindex(), "ObjectMannedPlasmagun.Fire" );
+ }
+// SetSentryAnim( TFTURRET_ANIM_FIRE );
+ DoMuzzleFlash();
+
+ --m_nAmmoCount;
+
+ m_flNextIdleTime = gpGlobals->curtime + MANNED_PLASMAGUN_IDLE_TIME;
+
+ // If I'm EMPed, slow the firing rate down
+ m_flNextAttack = gpGlobals->curtime + ( HasPowerup(POWERUP_EMP) ? 0.3f : 0.1f );
+}
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void CObjectMannedPlasmagun::PostDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PostDataUpdate( updateType );
+
+ bool teamchanged = GetTeamNumber() != m_nPreviousTeam;
+
+ if ( teamchanged ||
+ updateType == DATA_UPDATE_CREATED )
+ {
+ C_BaseAnimating::AllowBoneAccess( true, false );
+ SetupTeamModel();
+ C_BaseAnimating::AllowBoneAccess( false, false );
+ }
+}
+
+void CObjectMannedPlasmagun::PreDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PreDataUpdate( updateType );
+
+ m_nPreviousTeam = GetTeamNumber();
+}
+
+#endif
diff --git a/game/shared/tf2/tf_obj_manned_plasmagun.h b/game/shared/tf2/tf_obj_manned_plasmagun.h
new file mode 100644
index 0000000..9bd35c0
--- /dev/null
+++ b/game/shared/tf2/tf_obj_manned_plasmagun.h
@@ -0,0 +1,88 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A stationary gun that players can man
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_OBJ_MANNED_PLASMAGUN_H
+#define TF_OBJ_MANNED_PLASMAGUN_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_obj_base_manned_gun.h"
+
+#if defined( CLIENT_DLL )
+
+#define CObjectMannedPlasmagun C_ObjectMannedPlasmagun
+
+#endif
+
+// ------------------------------------------------------------------------ //
+// A stationary gun that players can man that's built by the player
+// ------------------------------------------------------------------------ //
+class CObjectMannedPlasmagun : public CObjectBaseMannedGun
+{
+public:
+#if !defined( CLIENT_DLL )
+ DECLARE_DATADESC();
+#endif
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+ DECLARE_CLASS( CObjectMannedPlasmagun, CObjectBaseMannedGun );
+
+ static CObjectMannedPlasmagun* Create(const Vector &vOrigin, const QAngle &vAngles);
+
+ CObjectMannedPlasmagun();
+
+ virtual void Spawn();
+ virtual void Precache();
+ virtual void SetupTeamModel( void );
+ virtual void FinishedBuilding( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ virtual void PreDataUpdate( DataUpdateType_t updateType );
+ virtual void PostDataUpdate( DataUpdateType_t updateType );
+#endif
+
+protected:
+ // Think function
+ void RechargeThink();
+
+ // Fire the weapon
+ virtual void Fire( void );
+
+protected:
+ int m_nMaxAmmoCount;
+ CNetworkVar( float, m_flNextIdleTime );
+
+ // Handling for the multiple barrels
+ int m_nRightBarrelAttachment;
+ CNetworkVar( bool, m_bFiringLeft );
+
+private:
+ CObjectMannedPlasmagun( const CObjectMannedPlasmagun& src );
+
+ int m_nPreviousTeam;
+};
+
+
+#endif // TF_OBJ_MANNED_PLASMAGUN_H
diff --git a/game/shared/tf2/tf_obj_manned_plasmagun_shared.cpp b/game/shared/tf2/tf_obj_manned_plasmagun_shared.cpp
new file mode 100644
index 0000000..9cbb4f1
--- /dev/null
+++ b/game/shared/tf2/tf_obj_manned_plasmagun_shared.cpp
@@ -0,0 +1,117 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A stationary gun that players can man
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "tf_obj_manned_plasmagun_shared.h"
+#include "tf_movedata.h"
+
+ConVar mannedgun_usethirdperson( "mannedgun_usethirdperson", "1", FCVAR_REPLICATED, "Use third person view while in manned guns built on vehicles." );
+
+#define MANNED_PLASMAGUN_AIMING_CONE_ANGLE 45.0f // total angle of aiming
+#define MANNED_PLASMAGUN_YAW_SPEED 1000.0f
+#define MANNED_PLASMAGUN_MAX_PITCH 50.0f
+#define MANNED_PLASMAGUN_BARREL_MAX_PITCH 30.0f
+
+float CObjectMannedPlasmagunMovement::GetMaxYaw() const
+{
+ return MANNED_PLASMAGUN_AIMING_CONE_ANGLE;
+}
+
+float CObjectMannedPlasmagunMovement::GetMinYaw() const
+{
+ return -MANNED_PLASMAGUN_AIMING_CONE_ANGLE;
+}
+
+float CObjectMannedPlasmagunMovement::GetMaxPitch() const
+{
+ return MANNED_PLASMAGUN_MAX_PITCH;
+}
+
+void CObjectMannedPlasmagunMovement::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove )
+{
+ CTFMoveData *pMoveData = (CTFMoveData*)pMove;
+ Assert( sizeof(MannedPlasmagunData_t) <= pMoveData->VehicleDataMaxSize() );
+
+ MannedPlasmagunData_t *pVehicleData = (MannedPlasmagunData_t*)pMoveData->VehicleData();
+
+ bool bSimple = (pVehicleData->m_nMoveStyle == MOVEMENT_STYLE_SIMPLE);
+ CBaseTFVehicle *pVehicle = pVehicleData->m_pVehicle;
+
+ // Flush caches since bone controllers might be wrong since they are not in the cache yet
+ pVehicle->InvalidateBoneCache();
+
+ // Get the view direction *in world coordinates*
+ Vector vPlayerEye = pPlayer->EyePosition();
+ QAngle angEyeAngles = pPlayer->LocalEyeAngles();
+ Vector vPlayerForward;
+ AngleVectors( angEyeAngles, &vPlayerForward, NULL, NULL );
+
+
+ // Now figure out the pitch. This is done by casting a ray to see where the player's aiming reticle is pointing.
+ // Then we do some trig to find out what angle the turret should point so it can see the target.
+ // NOTE: this is done in the tank's local space so it works when the tank is banked.
+ VMatrix mGunToWorld = SetupMatrixTranslation(pVehicle->GetAbsOrigin()) * SetupMatrixAngles(pVehicle->GetAbsAngles());
+ VMatrix mWorldToGun = mGunToWorld.InverseTR();
+
+ // First trace only on the world..
+ Vector start = vPlayerEye;
+ Vector end = start + vPlayerForward * 5000.0f;
+
+ Vector vTarget;
+ if ( bSimple )
+ {
+ vTarget = end;
+ }
+ else
+ {
+ trace_t trace;
+ UTIL_TraceLine(start, end, MASK_SOLID_BRUSHONLY, pVehicle, COLLISION_GROUP_NONE, &trace);
+ vTarget = trace.endpos;
+ if(trace.fraction == 1)
+ {
+ // It didn't hit the world, so trace on ents.
+ UTIL_TraceLine(start, end, MASK_PLAYERSOLID|MASK_NPCSOLID, pVehicle, COLLISION_GROUP_NONE, &trace);
+ vTarget = trace.endpos;
+ if(trace.fraction == 1)
+ {
+ // Didn't hit any ents.. just assume it's way out in front of the player's view.
+ vTarget = end;
+ }
+ }
+ }
+
+ // Transform the world position into gun space.
+ vTarget = mWorldToGun * vTarget;
+
+
+ // Compute the position of the barrel pivot point as measured in the coordinate system of the gun
+ Vector vTurretBase;
+ QAngle vTurretBaseAngles;
+ pVehicle->GetAttachment(pVehicleData->m_nBarrelPivotAttachment, vTurretBase, vTurretBaseAngles);
+
+ vTurretBase = mWorldToGun * vTurretBase;
+
+ // Make everything be relative to the pivot...
+ vTarget -= vTurretBase;
+
+
+ // Now we've got the target vector in local space. Now just figure out what
+ // the gun angles need to be to hit the target.
+ QAngle vWantedAngles;
+ VectorAngles( vTarget, vWantedAngles );
+
+ pVehicleData->m_flGunPitch = vWantedAngles[PITCH];
+ pVehicleData->m_flGunYaw = vWantedAngles[YAW];
+
+
+ // Place the player at the feet of the vehicle
+ Vector vStandAngles;
+
+ pVehicle->GetAttachmentLocal(pVehicleData->m_nStandAttachment, pMove->m_vecAbsOrigin, pMove->m_vecAngles);
+}
+
+
diff --git a/game/shared/tf2/tf_obj_manned_plasmagun_shared.h b/game/shared/tf2/tf_obj_manned_plasmagun_shared.h
new file mode 100644
index 0000000..396c0a4
--- /dev/null
+++ b/game/shared/tf2/tf_obj_manned_plasmagun_shared.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: A stationary gun that players can man
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_OBJ_MANNED_PLASMAGUN_SHARED_H
+#define TF_OBJ_MANNED_PLASMAGUN_SHARED_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetfvehicle.h"
+
+class CMoveData;
+
+enum MovementStyle_t
+{
+ MOVEMENT_STYLE_STANDARD = 0,
+ MOVEMENT_STYLE_BARREL_PIVOT = 1,
+ MOVEMENT_STYLE_SIMPLE = 2,
+};
+
+struct MannedPlasmagunData_t : public VehicleBaseMoveData_t
+{
+ float m_flGunYaw;
+ float m_flGunPitch;
+ float m_flBarrelPitch;
+ float m_flBarrelHeight;
+ int m_nBarrelPivotAttachment;
+ int m_nBarrelAttachment;
+ int m_nStandAttachment;
+ MovementStyle_t m_nMoveStyle;
+};
+
+
+class CObjectMannedPlasmagunMovement
+{
+public:
+ void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove );
+ float GetMaxYaw() const;
+ float GetMinYaw() const;
+ float GetMaxPitch() const;
+};
+
+
+
+#endif // TF_OBJ_MANNED_PLASMAGUN_SHARED_H
diff --git a/game/shared/tf2/tf_playeranimstate.h b/game/shared/tf2/tf_playeranimstate.h
new file mode 100644
index 0000000..9dcd249
--- /dev/null
+++ b/game/shared/tf2/tf_playeranimstate.h
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TF_PLAYERANIMSTATE_H
+#define TF_PLAYERANIMSTATE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "studio.h"
+
+#if defined( CLIENT_DLL )
+#define CBaseTFPlayer C_BaseTFPlayer
+#endif
+
+class CPlayerAnimState
+{
+public:
+ enum
+ {
+ TURN_NONE = 0,
+ TURN_LEFT,
+ TURN_RIGHT
+ };
+
+ CPlayerAnimState( CBaseTFPlayer *outer );
+
+ Activity BodyYawTranslateActivity( Activity activity );
+
+ void Update();
+
+ const QAngle& GetRenderAngles();
+
+ void GetPoseParameters( float poseParameter[MAXSTUDIOPOSEPARAM] );
+
+ CBaseTFPlayer *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( void );
+ void ComputePoseParam_BodyLookYaw( void );
+
+ void ComputePlaybackRate();
+
+ CBaseTFPlayer *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;
+};
+#endif // TF_PLAYERANIMSTATE_H
diff --git a/game/shared/tf2/tf_reconvars.cpp b/game/shared/tf2/tf_reconvars.cpp
new file mode 100644
index 0000000..dc1e2eb
--- /dev/null
+++ b/game/shared/tf2/tf_reconvars.cpp
@@ -0,0 +1,64 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_reconvars.h"
+
+// FIXME: put these in a config file.
+CReconJetpackLevel g_ReconJetpackLevels[MAX_TF_TECHLEVELS] =
+{
+ {
+ 0.35f, // How fast the jetpack recharges.
+ 1.0f, // How fast the jetpack depletes.
+ -0.1f, // When the jetpack is fully depleted, it snaps to this so the pilot sputters.
+ 150, // Fastest you can go upwards.
+ 15 // How fast it accelerates.
+ },
+
+ {
+ 0.55f, // How fast the jetpack recharges.
+ 0.75f, // How fast the jetpack depletes.
+ -0.1f, // When the jetpack is fully depleted, it snaps to this so the pilot sputters.
+ 150, // Fastest you can go upwards.
+ 15 // How fast it accelerates.
+ },
+
+ {
+ 0.55f, // How fast the jetpack recharges.
+ 0.55f, // How fast the jetpack depletes.
+ -0.1f, // When the jetpack is fully depleted, it snaps to this so the pilot sputters.
+ 200, // Fastest you can go upwards.
+ 15 // How fast it accelerates.
+ },
+
+ {
+ 0.75f, // How fast the jetpack recharges.
+ 0.35f, // How fast the jetpack depletes.
+ -0.1f, // When the jetpack is fully depleted, it snaps to this so the pilot sputters.
+ 200, // Fastest you can go upwards.
+ 15 // How fast it accelerates.
+ },
+
+ // Not used
+
+ {
+ 0.7f, // How fast the jetpack recharges.
+ 1.0f, // How fast the jetpack depletes.
+ -0.1f, // When the jetpack is fully depleted, it snaps to this so the pilot sputters.
+ 130, // Fastest you can go upwards.
+ 15 // How fast it accelerates.
+ },
+
+ {
+ 0.7f, // How fast the jetpack recharges.
+ 1.0f, // How fast the jetpack depletes.
+ -0.1f, // When the jetpack is fully depleted, it snaps to this so the pilot sputters.
+ 130, // Fastest you can go upwards.
+ 15 // How fast it accelerates.
+ },
+};
+
+
diff --git a/game/shared/tf2/tf_reconvars.h b/game/shared/tf2/tf_reconvars.h
new file mode 100644
index 0000000..6d87840
--- /dev/null
+++ b/game/shared/tf2/tf_reconvars.h
@@ -0,0 +1,31 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_RECONVARS_H
+#define TF_RECONVARS_H
+#pragma once
+
+
+#include "techtree.h"
+
+
+// Jetpack vars for each tech level.
+class CReconJetpackLevel
+{
+public:
+ float m_RechargeRate; // How fast the jetpack recharges.
+ float m_DepleteRate; // How fast the jetpack depletes.
+ float m_NegSnap; // When the jetpack is fully depleted, it snaps to this so the pilot sputters.
+ float m_MaxVerticalVel; // Fastest you can go upwards.
+ float m_AccelRate; // How fast it accelerates.
+};
+
+
+extern CReconJetpackLevel g_ReconJetpackLevels[MAX_TF_TECHLEVELS];
+
+
+#endif // TF_RECONVARS_H
diff --git a/game/shared/tf2/tf_shareddefs.cpp b/game/shared/tf2/tf_shareddefs.cpp
new file mode 100644
index 0000000..32d63be
--- /dev/null
+++ b/game/shared/tf2/tf_shareddefs.cpp
@@ -0,0 +1,605 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Data shared between the client & game dlls
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_shareddefs.h"
+#include "tier0/dbg.h"
+#include "basetypes.h"
+#include <KeyValues.h>
+
+#ifndef CLIENT_DLL
+
+ #include "tf_team.h"
+ #include "tf_class_commando.h"
+ #include "tf_class_defender.h"
+ #include "tf_class_escort.h"
+ #include "tf_class_infiltrator.h"
+ #include "tf_class_medic.h"
+ #include "tf_class_recon.h"
+ #include "tf_class_sniper.h"
+ #include "tf_class_support.h"
+ #include "tf_class_sapper.h"
+ #include "tf_class_pyro.h"
+
+#else
+
+ #include "c_tfteam.h"
+ #include "c_tf_class_commando.h"
+ #include "c_tf_class_defender.h"
+ #include "c_tf_class_escort.h"
+ #include "c_tf_class_infiltrator.h"
+ #include "c_tf_class_medic.h"
+ #include "c_tf_class_recon.h"
+ #include "c_tf_class_sniper.h"
+ #include "c_tf_class_support.h"
+ #include "c_tf_class_sapper.h"
+ #include "c_tf_class_pyro.h"
+
+#define CTFTeam C_TFTeam
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+ConVar inv_demo( "inv_demo","0", FCVAR_REPLICATED, "Invasion demo." );
+ConVar lod_effect_distance( "lod_effect_distance","3240000", FCVAR_REPLICATED, "Distance at which effects LOD." );
+ConVar tf_cheapobjects( "tf_cheapobjects","0", FCVAR_REPLICATED, "Set to 1 and all objects will cost 0" );
+
+
+//--------------------------------------------------------------------------
+// OBJECTS
+//--------------------------------------------------------------------------
+static int g_iClassInfo_Undecided[] =
+{
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Recon[] =
+{
+ OBJ_WAGON,
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Commando[] =
+{
+ OBJ_POWERPACK,
+ OBJ_VEHICLE_BOOST,
+ OBJ_DRAGONSTEETH,
+ OBJ_MANNED_MISSILELAUNCHER,
+ OBJ_SANDBAG_BUNKER,
+ OBJ_DRAGONSTEETH,
+
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Medic[] =
+{
+ OBJ_POWERPACK,
+ OBJ_SELFHEAL,
+ OBJ_BUFF_STATION,
+ OBJ_MANNED_PLASMAGUN,
+ OBJ_SANDBAG_BUNKER,
+ OBJ_BUNKER,
+ OBJ_DRAGONSTEETH,
+ OBJ_SHIELDWALL,
+
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Defender[] =
+{
+ OBJ_POWERPACK,
+ OBJ_SENTRYGUN_PLASMA,
+ OBJ_MANNED_MISSILELAUNCHER,
+ OBJ_BARBED_WIRE,
+ OBJ_DRAGONSTEETH,
+ OBJ_TOWER,
+ OBJ_SANDBAG_BUNKER,
+ OBJ_BUNKER,
+ OBJ_DRIVER_MACHINEGUN,
+ //OBJ_MORTAR,
+
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Sniper[] =
+{
+ OBJ_WAGON,
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Support[] =
+{
+ OBJ_WAGON,
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Escort[] =
+{
+ OBJ_SHIELDWALL,
+ OBJ_MANNED_SHIELD,
+ OBJ_SANDBAG_BUNKER,
+ OBJ_BUNKER,
+
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Sapper[] =
+{
+ OBJ_POWERPACK,
+ OBJ_DRAGONSTEETH,
+ OBJ_TOWER,
+ OBJ_SANDBAG_BUNKER,
+ OBJ_MANNED_PLASMAGUN,
+
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Infiltrator[] =
+{
+ OBJ_WAGON,
+ OBJ_LAST
+};
+
+static int g_iClassInfo_Pyro[] =
+{
+ OBJ_WAGON,
+ OBJ_LAST
+};
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool IsObjectAnUpgrade( int iObjectType )
+{
+ return ( iObjectType >= OBJ_SELFHEAL && iObjectType < OBJ_BATTERING_RAM );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool IsObjectAVehicle( int iObjectType )
+{
+ return ( iObjectType >= OBJ_BATTERING_RAM && iObjectType < OBJ_TOWER );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool IsObjectADefensiveBuilding( int iObjectType )
+{
+ return ( iObjectType >= OBJ_TOWER );
+}
+
+//--------------------------------------------------------------------------
+// PLAYER CLASSES
+//--------------------------------------------------------------------------
+
+#if defined( CLIENT_DLL )
+
+ #define DEFINE_PLAYERCLASS_ALLOC_FNS( className, iClass ) \
+ C_PlayerClass* AllocClient##className##( C_BaseTFPlayer *pPlayer ) \
+ { \
+ return new C_PlayerClass##className##( pPlayer ); \
+ } \
+ CPlayerClass* AllocServer##className##( CBaseTFPlayer *pPlayer ) \
+ { \
+ Assert( false ); \
+ return NULL; \
+ }
+
+ #define GENERATE_PLAYERCLASS_INFO( className ) \
+ AllocClient##className##, AllocServer##className, NULL
+
+
+ // ------------------------------------------------------------------------------------- //
+ // DT_AllPlayerClasses recv table.
+ // ------------------------------------------------------------------------------------- //
+
+ BEGIN_RECV_TABLE_NOBASE( C_AllPlayerClasses, DT_AllPlayerClasses )
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_COMMANDO]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassCommandoData ), DataTableRecvProxy_PointerDataTable ),
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_DEFENDER]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassDefenderData ), DataTableRecvProxy_PointerDataTable ),
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_ESCORT]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassEscortData ), DataTableRecvProxy_PointerDataTable ),
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_INFILTRATOR]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassInfiltratorData ), DataTableRecvProxy_PointerDataTable ),
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_MEDIC]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassMedicData ), DataTableRecvProxy_PointerDataTable ),
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_RECON]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassReconData ), DataTableRecvProxy_PointerDataTable ),
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_SNIPER]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassSniperData ), DataTableRecvProxy_PointerDataTable ),
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_SUPPORT]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassSupportData ), DataTableRecvProxy_PointerDataTable ),
+ RecvPropDataTable( RECVINFO_DT(m_pClasses[TFCLASS_SAPPER]), 0, &REFERENCE_RECV_TABLE( DT_PlayerClassSapperData ), DataTableRecvProxy_PointerDataTable )
+ END_RECV_TABLE()
+
+#else
+
+ #define DEFINE_PLAYERCLASS_ALLOC_FNS( className, iClass ) \
+ ConVar class_##className##_health( "class_" #className "_health", "0", FCVAR_NONE, #className "'s max health" ); \
+ C_PlayerClass* AllocClient##className##( C_BaseTFPlayer *pPlayer ) \
+ { \
+ Assert( false ); \
+ return NULL; \
+ } \
+ CPlayerClass* AllocServer##className##( CBaseTFPlayer *pPlayer ) \
+ { \
+ return new CPlayerClass##className##( pPlayer, iClass ); \
+ }
+
+ #define GENERATE_PLAYERCLASS_INFO( className ) \
+ AllocClient##className##, AllocServer##className, &class_##className##_health
+
+
+ // ------------------------------------------------------------------------------------- //
+ // DT_AllPlayerClasses recv table.
+ // ------------------------------------------------------------------------------------- //
+
+ BEGIN_SEND_TABLE_NOBASE( CAllPlayerClasses, DT_AllPlayerClasses )
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_COMMANDO]), &REFERENCE_SEND_TABLE( DT_PlayerClassCommandoData ), SendProxy_DataTablePtrToDataTable ),
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_DEFENDER]), &REFERENCE_SEND_TABLE( DT_PlayerClassDefenderData ), SendProxy_DataTablePtrToDataTable ),
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_ESCORT]), &REFERENCE_SEND_TABLE( DT_PlayerClassEscortData ), SendProxy_DataTablePtrToDataTable ),
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_INFILTRATOR]),&REFERENCE_SEND_TABLE( DT_PlayerClassInfiltratorData ), SendProxy_DataTablePtrToDataTable ),
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_MEDIC]), &REFERENCE_SEND_TABLE( DT_PlayerClassMedicData ), SendProxy_DataTablePtrToDataTable ),
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_RECON]), &REFERENCE_SEND_TABLE( DT_PlayerClassReconData ), SendProxy_DataTablePtrToDataTable ),
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_SNIPER]), &REFERENCE_SEND_TABLE( DT_PlayerClassSniperData ), SendProxy_DataTablePtrToDataTable ),
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_SUPPORT]), &REFERENCE_SEND_TABLE( DT_PlayerClassSupportData ), SendProxy_DataTablePtrToDataTable ),
+ SendPropDataTable( SENDINFO_DT(m_pClasses[TFCLASS_SAPPER]), &REFERENCE_SEND_TABLE( DT_PlayerClassSapperData ), SendProxy_DataTablePtrToDataTable )
+ END_SEND_TABLE()
+
+#endif
+
+
+
+// ------------------------------------------------------------------------------------- //
+// CAllPlayerClasses implementation.
+// ------------------------------------------------------------------------------------- //
+
+CAllPlayerClasses::CAllPlayerClasses( PLAYER_TYPE *pPlayer )
+{
+ for ( int i=0; i < TFCLASS_CLASS_COUNT; i++ )
+ {
+ m_pClasses[i] = NULL;
+
+#if defined( CLIENT_DLL )
+ if ( GetTFClassInfo( i )->m_pClientAlloc )
+ m_pClasses[i] = GetTFClassInfo( i )->m_pClientAlloc( pPlayer );
+#else
+ if ( GetTFClassInfo( i )->m_pServerAlloc )
+ m_pClasses[i] = GetTFClassInfo( i )->m_pServerAlloc( pPlayer );
+#endif
+ }
+}
+
+CAllPlayerClasses::~CAllPlayerClasses()
+{
+ for ( int i=0; i < TFCLASS_CLASS_COUNT; i++ )
+ {
+ delete m_pClasses[i];
+ }
+}
+
+PLAYER_CLASS_TYPE* CAllPlayerClasses::GetPlayerClass( int iClass )
+{
+ Assert( iClass >= 0 && iClass < TFCLASS_CLASS_COUNT );
+ return m_pClasses[iClass];
+}
+
+
+
+DEFINE_PLAYERCLASS_ALLOC_FNS( Recon, TFCLASS_RECON );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Commando, TFCLASS_COMMANDO );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Medic, TFCLASS_MEDIC );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Defender, TFCLASS_DEFENDER );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Sniper, TFCLASS_SNIPER );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Support, TFCLASS_SUPPORT );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Escort, TFCLASS_ESCORT );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Sapper, TFCLASS_SAPPER );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Infiltrator, TFCLASS_INFILTRATOR );
+DEFINE_PLAYERCLASS_ALLOC_FNS( Pyro, TFCLASS_PYRO );
+
+CTFClassInfo g_TFClassInfos[ TFCLASS_CLASS_COUNT ] =
+{
+ { "Undecided", g_iClassInfo_Undecided, false, NULL, NULL, NULL },
+ { "Recon", g_iClassInfo_Recon, false, GENERATE_PLAYERCLASS_INFO( Recon ) },
+ { "Commando", g_iClassInfo_Commando, true, GENERATE_PLAYERCLASS_INFO( Commando ) },
+ { "Medic", g_iClassInfo_Medic, true, GENERATE_PLAYERCLASS_INFO( Medic ) },
+ { "Defender", g_iClassInfo_Defender, true, GENERATE_PLAYERCLASS_INFO( Defender ) },
+ { "Sniper", g_iClassInfo_Sniper, false, GENERATE_PLAYERCLASS_INFO( Sniper ) },
+ { "Support", g_iClassInfo_Support, false, GENERATE_PLAYERCLASS_INFO( Support ) },
+ { "Escort", g_iClassInfo_Escort, true, GENERATE_PLAYERCLASS_INFO( Escort ) },
+ { "Sapper", g_iClassInfo_Sapper, true, GENERATE_PLAYERCLASS_INFO( Sapper ) },
+ { "Infiltrator",g_iClassInfo_Infiltrator, false, GENERATE_PLAYERCLASS_INFO( Infiltrator ) },
+ { "Pyro", g_iClassInfo_Pyro, false, GENERATE_PLAYERCLASS_INFO( Pyro ) }
+};
+
+
+const CTFClassInfo* GetTFClassInfo( int i )
+{
+ Assert( i >= 0 && i < TFCLASS_CLASS_COUNT );
+ return &g_TFClassInfos[i];
+}
+
+
+// ------------------------------------------------------------------------------------------------ //
+// CObjectInfo tables.
+// ------------------------------------------------------------------------------------------------ //
+
+CObjectInfo::CObjectInfo( char *pObjectName )
+{
+ m_pObjectName = pObjectName;
+ m_pClassName = NULL;
+ m_flBuildTime = -9999;
+ m_nMaxObjects = -9999;
+ m_Cost = -9999;
+ m_CostMultiplierPerInstance = -999;
+ m_UpgradeCost = -9999;
+ m_MaxUpgradeLevel = -9999;
+ m_pBuilderWeaponName = NULL;
+ m_pBuilderPlacementString = NULL;
+ m_SelectionSlot = -9999;
+ m_SelectionPosition = -9999;
+ m_bSolidToPlayerMovement = false;
+ m_flSapperAttachTime = -9999;
+ m_pIconActive = NULL;
+}
+
+
+CObjectInfo::~CObjectInfo()
+{
+ delete [] m_pClassName;
+ delete [] m_pStatusName;
+ delete [] m_pBuilderWeaponName;
+ delete [] m_pBuilderPlacementString;
+ delete [] m_pIconActive;
+}
+
+
+CObjectInfo g_ObjectInfos[OBJ_LAST] =
+{
+ CObjectInfo( "OBJ_POWERPACK" ),
+ CObjectInfo( "OBJ_RESUPPLY" ),
+ CObjectInfo( "OBJ_SENTRYGUN_PLASMA" ),
+ CObjectInfo( "OBJ_SENTRYGUN_ROCKET_LAUNCHER" ),
+ CObjectInfo( "OBJ_SHIELDWALL" ),
+ CObjectInfo( "OBJ_RESOURCEPUMP" ),
+ CObjectInfo( "OBJ_RESPAWN_STATION" ),
+ CObjectInfo( "OBJ_RALLYFLAG" ),
+ CObjectInfo( "OBJ_MANNED_PLASMAGUN" ),
+ CObjectInfo( "OBJ_MANNED_MISSILELAUNCHER" ),
+ CObjectInfo( "OBJ_MANNED_SHIELD" ),
+ CObjectInfo( "OBJ_EMPGENERATOR" ),
+ CObjectInfo( "OBJ_BUFF_STATION" ),
+ CObjectInfo( "OBJ_BARBED_WIRE" ),
+ CObjectInfo( "OBJ_MCV_SELECTION_PANEL" ),
+ CObjectInfo( "OBJ_MAPDEFINED" ),
+ CObjectInfo( "OBJ_MORTAR" ),
+ CObjectInfo( "OBJ_SELFHEAL" ),
+ CObjectInfo( "OBJ_ARMOR_UPGRADE" ),
+ CObjectInfo( "OBJ_VEHICLE_BOOST" ),
+ CObjectInfo( "OBJ_EXPLOSIVES" ),
+ CObjectInfo( "OBJ_DRIVER_MACHINEGUN" ),
+ CObjectInfo( "OBJ_BATTERING_RAM" ),
+ CObjectInfo( "OBJ_SIEGE_TOWER" ),
+ CObjectInfo( "OBJ_WAGON" ),
+ CObjectInfo( "OBJ_FLATBED" ),
+ CObjectInfo( "OBJ_VEHICLE_MORTAR" ),
+ CObjectInfo( "OBJ_VEHICLE_TELEPORT_STATION" ),
+ CObjectInfo( "OBJ_VEHICLE_TANK" ),
+ CObjectInfo( "OBJ_VEHICLE_MOTORCYCLE" ),
+ CObjectInfo( "OBJ_WALKER_STRIDER" ),
+ CObjectInfo( "OBJ_WALKER_MINI_STRIDER" ),
+ CObjectInfo( "OBJ_TOWER" ),
+ CObjectInfo( "OBJ_TUNNEL" ),
+ CObjectInfo( "OBJ_SANDBAG_BUNKER" ),
+ CObjectInfo( "OBJ_BUNKER" ),
+ CObjectInfo( "OBJ_DRAGONSTEETH" ),
+};
+
+
+char* ReadAndAllocStringValue( KeyValues *pSub, const char *pName, const char *pFilename )
+{
+ const char *pValue = pSub->GetString( pName, NULL );
+ if ( !pValue )
+ {
+ DevWarning( "Can't get key value '%s' from file '%s'.\n", pName, pFilename );
+ return "";
+ }
+
+ int len = Q_strlen( pValue ) + 1;
+ char *pAlloced = new char[ len ];
+ Assert( pAlloced );
+ Q_strncpy( pAlloced, pValue, len );
+ return pAlloced;
+}
+
+
+bool AreObjectInfosLoaded()
+{
+ return g_ObjectInfos[0].m_pClassName != NULL;
+}
+
+
+void LoadObjectInfos( IBaseFileSystem *pFileSystem )
+{
+ const char *pFilename = "scripts/objects.txt";
+
+ // Make sure this stuff hasn't already been loaded.
+ Assert( !AreObjectInfosLoaded() );
+
+ KeyValues *pValues = new KeyValues( "Object descriptions" );
+ if ( !pValues->LoadFromFile( pFileSystem, pFilename, "GAME" ) )
+ {
+ Error( "Can't open %s for object info.", pFilename );
+ pValues->deleteThis();
+ return;
+ }
+
+ // Now read each class's information in.
+ for ( int iObj=0; iObj < ARRAYSIZE( g_ObjectInfos ); iObj++ )
+ {
+ CObjectInfo *pInfo = &g_ObjectInfos[iObj];
+ KeyValues *pSub = pValues->FindKey( pInfo->m_pObjectName );
+ if ( !pSub )
+ {
+ Error( "Missing section '%s' from %s.", pInfo->m_pObjectName, pFilename );
+ pValues->deleteThis();
+ return;
+ }
+
+ // Read all the info in.
+ if ( (pInfo->m_flBuildTime = pSub->GetFloat( "BuildTime", -999 )) == -999 ||
+ (pInfo->m_nMaxObjects = pSub->GetInt( "MaxObjects", -999 )) == -999 ||
+ (pInfo->m_Cost = pSub->GetInt( "Cost", -999 )) == -999 ||
+ (pInfo->m_CostMultiplierPerInstance = pSub->GetFloat( "CostMultiplier", -999 )) == -999 ||
+ (pInfo->m_UpgradeCost = pSub->GetInt( "UpgradeCost", -999 )) == -999 ||
+ (pInfo->m_MaxUpgradeLevel = pSub->GetInt( "MaxUpgradeLevel", -999 )) == -999 ||
+ (pInfo->m_SelectionSlot = pSub->GetInt( "SelectionSlot", -999 )) == -999 ||
+ (pInfo->m_SelectionPosition = pSub->GetInt( "SelectionPosition", -999 )) == -999 ||
+ (pInfo->m_flSapperAttachTime = pSub->GetInt( "SapperAttachTime", -999 )) == -999 )
+ {
+ Error( "Missing data for object '%s' in %s.", pInfo->m_pObjectName, pFilename );
+ pValues->deleteThis();
+ return;
+ }
+
+ pInfo->m_pClassName = ReadAndAllocStringValue( pSub, "ClassName", pFilename );
+ pInfo->m_pStatusName = ReadAndAllocStringValue( pSub, "StatusName", pFilename );
+ pInfo->m_pBuilderWeaponName = ReadAndAllocStringValue( pSub, "BuilderWeaponName", pFilename );
+ pInfo->m_pBuilderPlacementString = ReadAndAllocStringValue( pSub, "BuilderPlacementString", pFilename );
+ pInfo->m_bSolidToPlayerMovement = pSub->GetInt( "SolidToPlayerMovement", 0 ) ? true : false;
+ pInfo->m_pIconActive = ReadAndAllocStringValue( pSub, "Icon", pFilename );
+ }
+
+ pValues->deleteThis();
+}
+
+
+const CObjectInfo* GetObjectInfo( int iObject )
+{
+ Assert( iObject >= 0 && iObject < OBJ_LAST );
+ Assert( AreObjectInfosLoaded() );
+ return &g_ObjectInfos[iObject];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the specified class is allowed to build the specified object type
+//-----------------------------------------------------------------------------
+bool ClassCanBuild( int iClass, int iObjectType )
+{
+ for ( int i = 0; i < OBJ_LAST; i++ )
+ {
+ // Hit the end?
+ if ( g_TFClassInfos[iClass].m_pClassObjects[i] == OBJ_LAST )
+ return false;
+
+ // Found it?
+ if ( g_TFClassInfos[iClass].m_pClassObjects[i] == iObjectType )
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the cost of another object of the specified type
+// If bLast is set, return the cost of the last built object of the specified type
+//-----------------------------------------------------------------------------
+
+int CalculateObjectCost( int iObjectType, int iNumberOfObjects, int iTeam, bool bLast )
+{
+ if ( tf_cheapobjects.GetInt() )
+ {
+ return 0;
+ }
+
+ // Find out how much the next object should cost
+ if ( bLast )
+ {
+ iNumberOfObjects = MAX(0,iNumberOfObjects-1);
+ }
+
+ int iCost = GetObjectInfo( iObjectType )->m_Cost;
+
+ // If a cost is negative, it means the first object of that type is free, and then
+ // it counts up as normal, using the negative value.
+ if ( iCost < 0 )
+ {
+ if ( iNumberOfObjects == 0 )
+ return 0;
+ iCost *= -1;
+ iNumberOfObjects--;
+ }
+
+ // MCVs have special rules: The team's first one is always free
+ if ( iObjectType == OBJ_VEHICLE_TELEPORT_STATION )
+ {
+ CTFTeam *pTeam = (CTFTeam *)GetGlobalTeam(iTeam);
+ if ( pTeam && pTeam->GetNumObjects(OBJ_VEHICLE_TELEPORT_STATION) == 0 )
+ {
+ iCost = 0;
+ }
+ }
+
+ // Human objects cost less across the board
+ if ( iTeam == TEAM_HUMANS )
+ {
+ iCost = ( ((float)iCost) * 0.8 );
+ }
+
+ // Calculate the cost based upon the number of objects
+ for ( int i = 0; i < iNumberOfObjects; i++ )
+ {
+ iCost *= GetObjectInfo( iObjectType )->m_CostMultiplierPerInstance;
+ }
+
+ return iCost;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the cost to upgrade an object of a specific type
+//-----------------------------------------------------------------------------
+int CalculateObjectUpgrade( int iObjectType, int iObjectLevel )
+{
+ // Max level?
+ if ( iObjectLevel >= GetObjectInfo( iObjectType )->m_MaxUpgradeLevel )
+ return 0;
+
+ int iCost = GetObjectInfo( iObjectType )->m_UpgradeCost;
+ for ( int i = 0; i < (iObjectLevel - 1); i++ )
+ {
+ iCost *= OBJECT_UPGRADE_COST_MULTIPLIER_PER_LEVEL;
+ }
+
+ return iCost;
+}
+
+//--------------------------------------------------------------------------
+// MORTAR
+//--------------------------------------------------------------------------
+// Names for each mortar ammo type
+char *MortarAmmoNames[ MA_LASTAMMOTYPE ] =
+{
+ "Normal Rounds",
+ //"Smoke Rounds",
+ "Cluster Rounds",
+ "Starburst Rounds",
+};
+
+// Techs needs for each mortar ammo type
+char *MortarAmmoTechs[ MA_LASTAMMOTYPE ] =
+{
+ "",
+ //"mortar_ammo_smoke",
+ "mortar_ammo_cluster",
+ "mortar_ammo_starburst",
+};
+
+// Max amounts of each mortar ammo type in a single mortar
+int MortarAmmoMax[ MA_LASTAMMOTYPE ] =
+{
+ -1, // -1 is infinite ammo
+ //20,
+ 20,
+ 10,
+};
diff --git a/game/shared/tf2/tf_shareddefs.h b/game/shared/tf2/tf_shareddefs.h
new file mode 100644
index 0000000..310ac15
--- /dev/null
+++ b/game/shared/tf2/tf_shareddefs.h
@@ -0,0 +1,663 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_SHAREDDEFS_H
+#define TF_SHAREDDEFS_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define MAX_TF_TEAMS 4
+
+extern ConVar inv_demo;
+extern ConVar lod_effect_distance;
+
+#include "const.h"
+
+//--------------------------------------------------------------------------
+// Teams
+#define TEAM_HUMANS 1
+#define TEAM_ALIENS 2
+
+//--------------------------------------------------------------------------
+// TF player flags.
+#define TF_PLAYER_HIDDEN (1<<0)
+#define TF_PLAYER_DAMAGE_BOOST (1<<1)
+#define TF_PLAYER_NUMFLAGS 2
+//--------------------------------------------------------------------------
+// Custom Kill types
+#define DMG_KILL_BULLRUSH 1
+
+
+//--------------
+// TF2 SPECIFIC DAMAGE FLAGS
+//--------------
+#define DMG_EMP (DMG_LASTGENERICFLAG<<1) // Hit by EMP
+#define DMG_PROBE (DMG_LASTGENERICFLAG<<2) // Doing a shield-aware probe (heal guns, emp guns)
+
+
+//--------------------------------------------------------------------------
+// Zone states
+#define ZONE_FRIENDLY 1
+#define ZONE_ENEMY 2
+#define ZONE_CONTESTED 3
+
+//--------------------------------------------------------------------------
+// Loot state
+#define LOOT_NOT 0
+#define LOOT_CAPABLE 1
+#define LOOT_LOOTING 2
+
+//--------------------------------------------------------------------------
+// Powerups
+//--------------------------------------------------------------------------
+enum
+{
+ POWERUP_BOOST, // Medic, buff station
+ POWERUP_EMP, // Technician
+ POWERUP_RUSH, // Rally flag
+ POWERUP_POWER, // Object power
+ MAX_POWERUPS
+};
+
+#define MAX_CABLE_CONNECTIONS 4
+
+//--------------------------------------------------------------------------
+// Acts
+//--------------------------------------------------------------------------
+#define MIN_ACT_OVERLAY_TIME 10.0
+
+// C/C_InfoAct spawnflags
+#define SF_ACT_INTERMISSION 1
+#define SF_ACT_WAITINGFORGAMESTART 2
+
+#define SF_ACT_BITS 2
+
+//--------------------------------------------------------------------------
+// Order types
+//--------------------------------------------------------------------------
+enum
+{
+ ORDER_NONE = 0,
+ ORDER_ATTACK, // Enemy held resource zone, attack it and capture it
+ ORDER_DEFEND, // Defend a resource zone we own
+ ORDER_CAPTURE, // Resource zone not held by either team, capture it
+ ORDER_KILL, // Kill a specific enemy player
+ ORDER_HEAL, // Heal a specific friendly player
+ ORDER_BUILD, // Order to build something.
+ // m_iStructure is one of the OBJ_ defines.
+ // If it's a sentry gun order, it's always OBJ_SENTRYGUN_PLASMA.
+ ORDER_REPAIR, // Repair a built item.
+ ORDER_MORTAR_ATTACK, // Build a mortar to shell an enemy object.
+ ORDER_ASSIST // Assist a player who is under attack.
+};
+
+
+//--------------------------------------------------------------------------
+// Collision groups
+//--------------------------------------------------------------------------
+enum
+{
+ TFCOLLISION_GROUP_SHIELD = LAST_SHARED_COLLISION_GROUP,
+ TFCOLLISION_GROUP_WEAPON,
+ TFCOLLISION_GROUP_GRENADE,
+ TFCOLLISION_GROUP_RESOURCE_CHUNK,
+ // Combat objects (override for above)
+ TFCOLLISION_GROUP_COMBATOBJECT,
+ // Objects in general
+ TFCOLLISION_GROUP_OBJECT,
+ TFCOLLISION_GROUP_OBJECT_SOLIDTOPLAYERMOVEMENT,
+};
+
+//--------------------------------------------------------------------------
+// MAP DEFINED OBJECTS
+//--------------------------------------------------------------------------
+#define MAX_OBJ_CUSTOMNAME_SIZE 128
+
+//--------------------------------------------------------------------------
+// OBJECTS
+//--------------------------------------------------------------------------
+enum
+{
+ OBJ_POWERPACK=0,
+ OBJ_RESUPPLY,
+ OBJ_SENTRYGUN_PLASMA, // Orders always refer to this type of sentry gun.
+ OBJ_SENTRYGUN_ROCKET_LAUNCHER,
+ OBJ_SHIELDWALL,
+ OBJ_RESOURCEPUMP,
+ OBJ_RESPAWN_STATION,
+ OBJ_RALLYFLAG,
+ OBJ_MANNED_PLASMAGUN,
+ OBJ_MANNED_MISSILELAUNCHER,
+ OBJ_MANNED_SHIELD,
+ OBJ_EMPGENERATOR,
+ OBJ_BUFF_STATION,
+ OBJ_BARBED_WIRE,
+ OBJ_MCV_SELECTION_PANEL,
+ OBJ_MAPDEFINED,
+ OBJ_MORTAR,
+ // ADD STANDARD OBJECTS HERE
+
+ // Upgrades
+ OBJ_SELFHEAL,
+ OBJ_ARMOR_UPGRADE,
+ OBJ_VEHICLE_BOOST,
+ OBJ_EXPLOSIVES,
+ OBJ_DRIVER_MACHINEGUN,
+ // ADD UPGRADES HERE
+
+ // Vehicles
+ OBJ_BATTERING_RAM,
+ OBJ_SIEGE_TOWER,
+ OBJ_WAGON,
+ OBJ_FLATBED,
+ OBJ_VEHICLE_MORTAR,
+ OBJ_VEHICLE_TELEPORT_STATION,
+ OBJ_VEHICLE_TANK,
+ OBJ_VEHICLE_MOTORCYCLE,
+ OBJ_WALKER_STRIDER,
+ OBJ_WALKER_MINI_STRIDER,
+ // ADD VEHICLES HERE
+
+ // Defensive buildings
+ OBJ_TOWER,
+ OBJ_TUNNEL,
+ OBJ_SANDBAG_BUNKER,
+ OBJ_BUNKER,
+ OBJ_DRAGONSTEETH,
+ // ADD DEFENSIVE-ONLY BUILDINGS HERE
+
+ // If you add a new object, you need to add it to the g_ObjectInfos array
+ // in tf_shareddefs.cpp, and add it's data to the scripts/object.txt
+
+ OBJ_LAST,
+};
+
+#define OBJECT_COST_MULTIPLIER_PER_OBJECT 3
+#define OBJECT_UPGRADE_COST_MULTIPLIER_PER_LEVEL 3
+
+bool IsObjectAnUpgrade( int iObjectType );
+bool IsObjectAVehicle( int iObjectType );
+bool IsObjectADefensiveBuilding( int iObjectType );
+
+class CHudTexture;
+
+class CObjectInfo
+{
+public:
+ CObjectInfo( char *pObjectName );
+ ~CObjectInfo();
+
+ // This is initialized by the code and matched with a section in objects.txt
+ char *m_pObjectName;
+
+ // This stuff all comes from objects.txt
+ char *m_pClassName; // Code classname (in LINK_ENTITY_TO_CLASS).
+ char *m_pStatusName; // Shows up when crosshairs are on the object.
+ float m_flBuildTime;
+ int m_nMaxObjects; // Maximum number of objects per player
+ int m_Cost; // Base object resource cost
+ float m_CostMultiplierPerInstance; // Cost multiplier
+ int m_UpgradeCost; // Base object resource cost for upgrading
+ int m_MaxUpgradeLevel; // Max object upgrade level
+ char *m_pBuilderWeaponName; // Names shown for each object onscreen when using the builder weapon
+ char *m_pBuilderPlacementString; // String shown to player during placement of this object
+ int m_SelectionSlot; // Weapon selection slots for objects
+ int m_SelectionPosition; // Weapon selection positions for objects
+ bool m_bSolidToPlayerMovement;
+ float m_flSapperAttachTime; // Time it takes to place a sapper on this object
+
+ // HUD weapon selection menu icon ( from hud_textures.txt )
+ char *m_pIconActive;
+};
+
+// Loads the objects.txt script.
+class IBaseFileSystem;
+void LoadObjectInfos( IBaseFileSystem *pFileSystem );
+
+// Get a CObjectInfo from a TFOBJ_ define.
+const CObjectInfo* GetObjectInfo( int iObject );
+
+
+// Object utility funcs
+bool ClassCanBuild( int iClass, int iObjectType );
+int CalculateObjectCost( int iObjectType, int iNumberOfObjects, int iTeam, bool bLast = false );
+int CalculateObjectUpgrade( int iObjectType, int iObjectLevel );
+
+//--------------------------------------------------------------------------
+// OBJECT FLAGS
+//--------------------------------------------------------------------------
+enum
+{
+ OF_SUPPRESS_APPEAR_ON_MINIMAP = 0x0001,
+ OF_SUPPRESS_NOTIFY_UNDER_ATTACK = 0x0002,
+ OF_SUPPRESS_VISIBLE_TO_TACTICAL = 0x0004,
+ OF_ALLOW_REPEAT_PLACEMENT = 0x0008,
+ OF_SUPPRESS_TECH_ANALYZER = 0x0010,
+ OF_DONT_AUTO_REPAIR = 0x0020,
+ OF_ALIGN_TO_GROUND = 0x0040, // Align my angles to match the ground underneath me
+ OF_DONT_PREVENT_BUILD_NEAR_OBJ = 0x0080, // Don't prevent building if there's another object nearby
+ OF_CAN_BE_PICKED_UP = 0x0100,
+ OF_DOESNT_NEED_POWER = 0x0200, // Doesn't need power, even on the human team
+ OF_DOESNT_HAVE_A_MODEL = 0x0400, // It's built from map placed geometry
+ OF_MUST_BE_BUILT_IN_CONSTRUCTION_YARD = 0x0800,
+ OF_MUST_BE_BUILT_IN_RESOURCE_ZONE = 0x1000,
+ OF_MUST_BE_BUILT_ON_ATTACHMENT = 0x2000,
+ OF_CANNOT_BE_DISMANTLED = 0x4000,
+
+ OF_BIT_COUNT = 15
+};
+
+
+//--------------------------------------------------------------------------
+// Builder "weapon" states
+//--------------------------------------------------------------------------
+enum
+{
+ BS_IDLE = 0,
+ BS_SELECTING,
+ BS_PLACING,
+ BS_PLACING_INVALID,
+ BS_BUILDING,
+ BS_REPAIR,
+};
+
+
+//--------------------------------------------------------------------------
+// Builder object id...
+//--------------------------------------------------------------------------
+enum
+{
+ BUILDER_OBJECT_BITS = 8,
+ BUILDER_INVALID_OBJECT = ((1 << BUILDER_OBJECT_BITS) - 1)
+};
+
+// Analyzer state
+enum
+{
+ AS_INACTIVE = 0,
+ AS_SUBVERTING,
+ AS_ANALYZING
+};
+
+// Max number of objects a team can have
+#define MAX_OBJECTS_PER_TEAM 512
+#define MAX_OBJECTS_PER_PLAYER 64
+
+
+//--------------------------------------------------------------------------
+// BUILDING
+//--------------------------------------------------------------------------
+// Build checks will return one of these for a player
+enum
+{
+ CB_CAN_BUILD, // Player is allowed to build this object
+ CB_NOT_RESEARCHED, // Player's team hasn't researched this object
+ CB_LIMIT_REACHED, // Player has reached the limit of the number of these objects allowed
+ CB_NEED_RESOURCES, // Player doesn't have enough resources to build this object
+ CB_NEED_ADRENALIN, // Commando doesn't have enough adrenalin to build a rally flag
+};
+
+//--------------------------------------------------------------------------
+// MORTAR
+//--------------------------------------------------------------------------
+// Mortar Firing States
+enum
+{
+ MORTAR_IDLE,
+ MORTAR_CHARGING_POWER,
+ MORTAR_CHARGING_ACCURACY,
+};
+
+// Mortar salvos
+#define MORTAR_SALVO_SIZE 5
+#define MORTAR_RELOAD_TIME 5.0
+
+// Mortar firing details
+#define MORTAR_RANGE_MIN 1024
+#define MORTAR_RANGE_MAX_INITIAL 3000
+#define MORTAR_RANGE_MAX_UPGRADED 5000
+
+// Inaccuracy Data
+// These values are all max inaccuracies. The accuracy used is between 0 & Max, based upon how close the player gets to hitting
+// the fire button at the right time on the mortar firing slider bar.
+// Perpendicular-to-the-shot inaccuracy
+#define MORTAR_INACCURACY_MAX_INITIAL 0.85 // Percentage of distance that a mortar can be wide of the mark
+#define MORTAR_INACCURACY_MAX_UPGRADED 0.35 // Percentage of distance that a mortar can be wide of the mark
+// Distance inaccuracy
+#define MORTAR_DIST_INACCURACY 0.3 // Percentage of distance that the mortar can deviate
+
+// Mortar firing details
+#define MORTAR_CHARGE_POWER_RATE 1.5 // Time taken to hit full charge for power
+#define MORTAR_CHARGE_ACCURACY_RATE 1.25 // Time taken to hit perfect accuracy, given a half-power shot
+
+// Mortar ammo types
+enum MortarAmmoType
+{
+ MA_SHELL = 0, // Normal mortar round
+ //MA_SMOKE, // Smoke mortar round
+ MA_CLUSTER, // Mirv mortar round
+ MA_STARBURST, // Starburst / phosphorous mortar round
+
+ MA_LASTAMMOTYPE,
+};
+
+extern char *MortarAmmoNames[ MA_LASTAMMOTYPE ];
+extern char *MortarAmmoTechs[ MA_LASTAMMOTYPE ];
+extern int MortarAmmoMax[ MA_LASTAMMOTYPE ];
+
+//--------------------------------------------------------------------------
+// ROCKET PACK
+//--------------------------------------------------------------------------
+#define RP_LOCK_TIME 3.0 // Time taken to lock onto a target
+
+//--------------------------------------------------------------------------
+// PARTICLE BEAM
+//--------------------------------------------------------------------------
+#define PB_RECHARGE_TIME 30.0 // Time taken to recharge the particle beam
+
+//--------------------------------------------------------------------------
+// PLASMA PROJECTILE TYPES
+//--------------------------------------------------------------------------
+enum
+{
+ PLASMATYPE_GATLING,
+ PLASMATYPE_EMP,
+ PLASMATYPE_GUIDED,
+ PLASMATYPE_GUIDED_NOTARGET,
+ PLASMATYPE_GUIDED_PARRIED,
+ PLASMATYPE_PLASMABALL,
+ PLASMATYPE_PLASMABALL_EXPLOSIVE,
+};
+
+#define PLASMA_VELOCITY ( 2500 )
+
+//--------------------------------------------------------------------------
+// SCANNERS
+//--------------------------------------------------------------------------
+// Ranges
+#define SCANNER_RANGE 3000
+#define LOCAL_PLAYER_SCANNER_RANGE 1500
+
+//--------------------------------------------------------------------------
+// EMP
+//--------------------------------------------------------------------------
+#define EMP_HITSCAN_DURATION 5.0f
+
+//--------------------------------------------------------------------------
+// KNOCKDOWN
+//--------------------------------------------------------------------------
+// Knockdown blend in and out times
+#define KNOCKDOWN_BLEND_IN ( 0.3f )
+#define KNOCKDOWN_BLEND_OUT ( 0.5f )
+
+//--------------------------------------------------------------------------
+// THERMAL VISION
+//--------------------------------------------------------------------------
+// Thermal vision radius
+// Players outside of this radius will not be sent to the local player
+// Player's inside start to fade at the startfade distance and are alpha'd out completely
+// at the full radius
+#define THERMAL_VISION_RADIUS ( 1024.0f )
+#define THERMAL_VISION_STARTFADE ( THERMAL_VISION_RADIUS / 2.0f )
+
+//--------------------------------------------------------------------------
+// CAMO
+//--------------------------------------------------------------------------
+// Infiltrator Camouflage constants
+// # of seconds to go into/back into camo mode
+#define CAMO_ENABLETIME ( 3.0f )
+// # of seconds to remove
+#define CAMO_REMOVETIME ( 1.0f )
+// # of seconds to temporarily suppress
+#define CAMO_SUPPRESSTIME ( 1.0f )
+
+// Outside this, exclude from PVS
+#define CAMO_OUTER_RADIUS ( 2048.0f )
+// From here to outer, just alpha to 0
+#define CAMO_INNER_RADIUS ( CAMO_OUTER_RADIUS / 2.0f )
+// 75 % opacity at the inner_radius
+#define CAMO_INNER_ALPHA ( 192 )
+// From here to inner fade alpha again and start using invis effect
+#define CAMO_INVIS_RADIUS ( CAMO_INNER_RADIUS / 2.0f )
+
+// Infiltrator's phaseout duration
+#define INFILTRATOR_PHASEOUT_DURATION 30.0f
+// Time required to recharge
+#define INFILTRATOR_PHASEOUT_RECHARGETIME 30.0f
+
+// After sitting still, remove from tactical and minimiap starting at this time
+#define SNIPER_STATIONARY_FADESTART 2.5f
+// After sitting still, remove from tactical and minimiap starting and finishing here
+#define SNIPER_STATIONARY_FADEFINISH 7.5f
+
+// Infiltrator Camouflage constants
+// # of seconds to go into/back into camo mode
+#define CAMO_ENABLETIME ( 3.0f )
+// # of seconds to remove
+#define CAMO_REMOVETIME ( 1.0f )
+// # of seconds to temporarily suppress
+#define CAMO_SUPPRESSTIME ( 1.0f )
+
+// Outside this, exclude from PVS
+#define CAMO_OUTER_RADIUS ( 2048.0f )
+// From here to outer, just alpha to 0
+#define CAMO_INNER_RADIUS ( CAMO_OUTER_RADIUS / 2.0f )
+// 75 % opacity at the inner_radius
+#define CAMO_INNER_ALPHA ( 192 )
+// From here to inner fade alpha again and start using invis effect
+#define CAMO_INVIS_RADIUS ( CAMO_INNER_RADIUS / 2.0f )
+
+// Infiltrator's phaseout duration
+#define INFILTRATOR_PHASEOUT_DURATION 30.0f
+// Time required to recharge
+#define INFILTRATOR_PHASEOUT_RECHARGETIME 30.0f
+
+// After sitting still, remove from tactical and minimiap starting at this time
+#define SNIPER_STATIONARY_FADESTART 2.5f
+// After sitting still, remove from tactical and minimiap starting and finishing here
+#define SNIPER_STATIONARY_FADEFINISH 7.5f
+
+
+//--------------------------------------------------------------------------
+// ANIM STATEMACHINE DEFINES
+//--------------------------------------------------------------------------
+// Sniper deploy node
+enum
+{
+ SNIPER_DEPLOY_START = 1,
+ SNIPER_DEPLOY_IDLE,
+ SNIPER_DEPLOY_LEAVE,
+};
+
+//--------------------------------------------------------------------------
+// COMBAT SHIELD
+//--------------------------------------------------------------------------
+#define SHIELD_HITGROUP 1
+
+// Length after raising the shield during which releasing the shield will cause a parry
+#define PARRY_DETECTION_TIME 0.5
+// Length after a parry detection in which a parry can occur
+#define PARRY_OPPORTUNITY_LENGTH 0.3
+// Length after a parry has finished before I can do anything again
+#define PARRY_VULNERABLE_TIME 0.5
+
+//--------------------------------------------------------------------------
+// SENTRYGUNS
+//--------------------------------------------------------------------------
+// Time it takes to turtle / unturtle
+#define SENTRY_TURTLE_TIME 2.0
+
+//--------------------------------------------------------------------------
+// PORTABLE POWER GENERATOR - BUFF STATION
+//--------------------------------------------------------------------------
+#define BUFF_STATION_MAX_PLAYERS 4
+#define BUFF_STATION_MAX_PLAYER_BITS 3
+#define BUFF_STATION_MAX_OBJECTS 3
+#define BUFF_STATION_MAX_OBJECT_BITS 2
+
+//--------------------------------------------------------------------------
+// ADRENALIN
+//--------------------------------------------------------------------------
+// Animation speed while in adrenalin
+#define ADRENALIN_ANIM_SPEED 1.5
+
+//--------------------------------------------------------------------------
+// PLASMA RIFLE
+//--------------------------------------------------------------------------
+#define MAX_RIFLE_POWER 3.0
+#define RIFLE_CHARGE_TIME 2.0
+
+//--------------------------------------------------------------------------
+// HUMAN POWER PACKS
+//--------------------------------------------------------------------------
+#define MAX_OBJECTS_PER_PACK 3
+
+//--------------------------------------------------------------------------
+// Rally flag defines
+//--------------------------------------------------------------------------
+
+#define RALLYFLAG_MINS Vector(-20, -20, 0)
+#define RALLYFLAG_MAXS Vector( 20, 20, 90)
+#define RALLYFLAG_RADIUS 512
+#define RALLYFLAG_LIFETIME 30
+#define RALLYFLAG_RATE 2 // Rate at which it looks for friendlies to rally
+#define RALLYFLAG_ADRENALIN_TIME 5 // Time an adrenalin rush lasts
+#define RALLYFLAG_MODEL "models/props/common/holo_banner/holo_banner.mdl"
+
+
+//--------------------------------------------------------------------------
+// Resupply-related stuff
+//--------------------------------------------------------------------------
+enum ResupplyBuyType_t
+{
+ RESUPPLY_BUY_AMMO = 0,
+ RESUPPLY_BUY_HEALTH,
+ RESUPPLY_BUY_GRENADES,
+ RESUPPLY_BUY_ALL,
+
+ RESUPPLY_BUY_TYPE_COUNT
+};
+
+#define RESUPPLY_HEALTH_COST 20
+#define RESUPPLY_AMMO_COST 5
+#define RESUPPLY_GRENADES_COST 25
+#define RESUPPLY_ALL_COST 50
+#define RESUPPLY_ROCKET_COST 100
+
+// Build animation events
+#define TF_OBJ_ENABLEBODYGROUP 6000
+#define TF_OBJ_DISABLEBODYGROUP 6001
+#define TF_OBJ_ENABLEALLBODYGROUPS 6002
+#define TF_OBJ_DISABLEALLBODYGROUPS 6003
+#define TF_OBJ_PLAYBUILDSOUND 6004
+
+//--------------------------------------------------------------------------
+// Class id
+//--------------------------------------------------------------------------
+
+#include "tfclassdata_shared.h"
+
+
+//--------------------------------------------------------------------------
+//
+// Class info tables. Any time a class is added or removed, this is where
+// generic data about each class is stored.
+//
+// The other things you need to add or remove for a class are:
+//
+// - A class derived from CPlayerClass.
+// - Class-specific accessors and data members functions in CTFMoveData.
+// - tf_gamemovement_chooser.h and CTFGameMovementChooser::CTFGameMovementChooser().
+// - DEFINE_PRED_TYPEDESCRIPTION_PTR entries in c_basetfplayer.cpp
+// - Add class_X_health in skill1.cfg.
+//
+//--------------------------------------------------------------------------
+
+class CPlayerClass;
+class CBaseTFPlayer;
+typedef CPlayerClass* (*PlayerClassAllocFn_Server)( CBaseTFPlayer *pPlayer );
+
+class C_PlayerClass;
+class C_BaseTFPlayer;
+typedef C_PlayerClass* (*PlayerClassAllocFn_Client)( C_BaseTFPlayer *pPlayer );
+
+
+class ConVar;
+
+
+class CTFClassInfo
+{
+public:
+ char *m_pClassName;
+
+ // Objects that each class can build
+ // OBJ_X, OBJ_Y ... terminated with OBJ_LAST.
+ int *m_pClassObjects;
+
+ // This is just to make stats gathering easy... which classes are in the game right now?
+ bool m_pCurrentlyActive;
+
+ PlayerClassAllocFn_Client m_pClientAlloc; // Only valid in client.dll
+ PlayerClassAllocFn_Server m_pServerAlloc; // Only valid in the game dll
+
+ ConVar *m_pMaxHealthCVar; // Only valid in game dll
+};
+
+const CTFClassInfo* GetTFClassInfo( int i );
+
+
+#if defined( CLIENT_DLL )
+
+ #define CAllPlayerClasses C_AllPlayerClasses
+ #define PLAYER_CLASS_TYPE C_PlayerClass
+ #define PLAYER_TYPE C_BaseTFPlayer
+
+ EXTERN_RECV_TABLE( DT_AllPlayerClasses );
+
+#else
+
+ #define PLAYER_CLASS_TYPE CPlayerClass
+ #define PLAYER_TYPE CBaseTFPlayer
+
+ EXTERN_SEND_TABLE( DT_AllPlayerClasses );
+
+#endif
+
+
+class PLAYER_TYPE;
+class PLAYER_CLASS_TYPE;
+
+//
+// The player object contains this on both the client and the server.
+// It holds a copy of each player class that can be used.
+//
+class CAllPlayerClasses // (#define as C_AllPlayerClasses on the client)
+{
+public:
+ CAllPlayerClasses( PLAYER_TYPE *pPlayer );
+ ~CAllPlayerClasses();
+
+ PLAYER_CLASS_TYPE* GetPlayerClass( int iClass );
+
+public:
+
+ PLAYER_CLASS_TYPE* m_pClasses[ TFCLASS_CLASS_COUNT ];
+};
+
+//--------------------------------------------------------------------------
+// Impact data
+//--------------------------------------------------------------------------
+// This sucks
+#define NUM_WOOD_GIBS_SMALL 5
+extern const char *ImpactHurtGibs_Wood_Small[ NUM_WOOD_GIBS_SMALL ];
+
+#define PLAYER_MSG_PERSONAL_SHIELD 2
+
+#endif // TF_SHAREDDEFS_H
+
diff --git a/game/shared/tf2/tf_shield_mobile_shared.cpp b/game/shared/tf2/tf_shield_mobile_shared.cpp
new file mode 100644
index 0000000..e5d153b
--- /dev/null
+++ b/game/shared/tf2/tf_shield_mobile_shared.cpp
@@ -0,0 +1,850 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The Escort's Shield weapon
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "in_buttons.h"
+#include "tf_shieldshared.h"
+#include "tf_shareddefs.h"
+#include "baseentity_shared.h"
+
+#if defined( CLIENT_DLL )
+
+#include "c_shield.h"
+
+#else
+
+#include "tf_shield.h"
+#include "gamerules.h"
+
+#endif
+
+
+#if defined( CLIENT_DLL )
+#define CShieldMobile C_ShieldMobile
+#define CShield C_Shield
+#endif
+
+
+//-----------------------------------------------------------------------------
+// ConVars
+//-----------------------------------------------------------------------------
+ConVar shield_mobile_power( "shield_mobile_power","30", FCVAR_REPLICATED, "Max power level of a escort's mobile projected shield." );
+ConVar shield_mobile_recharge_delay( "shield_mobile_recharge_delay","0.1", FCVAR_REPLICATED, "Time after taking damage before mobile projected shields begin to recharge." );
+ConVar shield_mobile_recharge_amount( "shield_mobile_recharge_amount","2", FCVAR_REPLICATED, "Power recharged each recharge tick for mobile projected shields." );
+ConVar shield_mobile_recharge_time( "shield_mobile_recharge_time","0.5", FCVAR_REPLICATED, "Time between each recharge tick for mobile projected shields." );
+
+
+#define EMP_WAVE_AMPLITUDE 8.0f
+
+
+//-----------------------------------------------------------------------------
+// Mobile version of the shield
+//-----------------------------------------------------------------------------
+class CShieldMobile;
+class CShieldMobileActiveVertList : public IActiveVertList
+{
+public:
+ void Init( CShieldMobile *pShield );
+
+// IActiveVertList overrides.
+public:
+
+ virtual int GetActiveVertState( int iVert );
+ virtual void SetActiveVertState( int iVert, int bOn );
+
+private:
+ CShieldMobile *m_pShield;
+};
+
+
+//-----------------------------------------------------------------------------
+// Mobile version of the shield
+//-----------------------------------------------------------------------------
+class CShieldMobile : public CShield, public IEntityEnumerator
+{
+ DECLARE_CLASS( CShieldMobile, CShield );
+
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ friend class CShieldMobileActiveVertList;
+
+ CShieldMobile();
+
+#ifndef CLIENT_DLL
+ DECLARE_DATADESC();
+#endif
+
+public:
+ void Spawn( void );
+ void Precache( void );
+ void ShieldThink( void );
+ virtual void ClientThink();
+ virtual void SetAngularSpringConstant( float flConstant );
+ virtual void SetFrontDistance( float flDistance );
+ virtual void ComputeWorldSpaceSurroundingBox( Vector *pWorldMins, Vector *pWorldMaxs );
+
+ virtual void SetAttachmentIndex( int nAttachmentIndex );
+ virtual void SetEMPed( bool isEmped );
+ virtual void SetAlwaysOrient( bool bOrient );
+ virtual bool IsAlwaysOrienting( );
+
+ virtual int Width();
+ virtual int Height();
+ virtual bool IsPanelActive( int x, int y );
+ virtual const Vector& GetPoint( int x, int y );
+ virtual void SetCenterAngles( const QAngle& angles );
+ virtual void SetThetaPhi( float flTheta, float flPhi );
+
+ virtual void GetRenderBounds( Vector& mins, Vector& maxs );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+public:
+#ifdef CLIENT_DLL
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void GetBounds( Vector& mins, Vector& maxs );
+ virtual void AddEntity( );
+ virtual void GetShieldData( const Vector** ppVerts, float* pOpacity, float* pBlend );
+
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwnerEntity() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+#endif
+
+public:
+ // Inherited from IEntityEnumerator
+ virtual bool EnumEntity( IHandleEntity *pHandleEntity );
+
+private:
+ // Teleport!
+ void OnTeleported( );
+ void SimulateShield( void );
+
+private:
+ struct SweepContext_t
+ {
+ SweepContext_t( const CBaseEntity *passentity, int collisionGroup ) :
+ m_Filter( passentity, collisionGroup ) {}
+
+ CTraceFilterSimple m_Filter;
+ Vector m_vecStartDelta;
+ Vector m_vecEndDelta;
+ };
+
+ enum
+ {
+ NUM_SUBDIVISIONS = 21,
+ };
+
+ enum
+ {
+ SHIELD_ORIENT_TO_OWNER = 0x2
+ };
+
+private:
+ CShieldMobile( const CShieldMobile & );
+ void ComputeBoundingBox( void );
+ void DetermineObstructions( );
+
+private:
+#ifdef CLIENT_DLL
+ // Is a particular panel an edge?
+ bool IsVertexValid( float s, float t ) const;
+ void PreRender( );
+#endif
+
+private:
+ CShieldMobileActiveVertList m_VertList;
+ QAngle m_tmpAngLockedAngles;
+
+ // Bitfield indicating which vertices are active
+ CShieldEffect m_ShieldEffect;
+ CNetworkArray( unsigned char, m_pVertsActive, SHIELD_NUM_CONTROL_POINTS >> 3 );
+ CNetworkVar( unsigned char, m_ShieldState );
+ CNetworkVar( float, m_flFrontDistance );
+ CNetworkVar( QAngle, m_angLockedAngles );
+ SweepContext_t *m_pEnumCtx;
+
+ // This is the width + height of the shield, not the current theta, phi
+ CNetworkVar( float, m_flShieldTheta );
+ CNetworkVar( float, m_flShieldPhi );
+ CNetworkVar( float, m_flSpringConstant );
+ CNetworkVar( int, m_nAttachmentIndex );
+};
+
+
+//=============================================================================
+// Shield effect
+//=============================================================================
+#ifndef CLIENT_DLL
+
+BEGIN_DATADESC( CShieldMobile )
+
+ DEFINE_THINKFUNC( ShieldThink ),
+
+END_DATADESC()
+
+#endif
+
+LINK_ENTITY_TO_CLASS( shield_mobile, CShieldMobile );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( ShieldMobile, DT_ShieldMobile );
+
+// -------------------------------------------------------------------------------- //
+// This data only gets sent to clients that ARE this player entity.
+// -------------------------------------------------------------------------------- //
+
+BEGIN_NETWORK_TABLE(CShieldMobile, DT_ShieldMobile)
+#if !defined( CLIENT_DLL )
+ SendPropInt (SENDINFO(m_ShieldState), 2, SPROP_UNSIGNED ),
+ SendPropArray(
+ SendPropInt( SENDINFO_ARRAY(m_pVertsActive), 8, SPROP_UNSIGNED),
+ m_pVertsActive),
+
+ SendPropFloat( SENDINFO(m_flFrontDistance), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flShieldTheta), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flShieldPhi), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flSpringConstant), 0, SPROP_NOSCALE ),
+ SendPropQAngles( SENDINFO(m_angLockedAngles), 9 ),
+ SendPropInt (SENDINFO(m_nAttachmentIndex), 10, SPROP_UNSIGNED ),
+
+ // Don't bother sending these, they are totally controlled by the think function
+ SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
+ SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[0]" ),
+ SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[1]" ),
+ SendPropExclude( "DT_BaseEntity", "m_angAbsRotation[2]" ),
+
+#else
+ RecvPropInt( RECVINFO(m_ShieldState) ),
+ RecvPropArray(
+ RecvPropInt( RECVINFO(m_pVertsActive[0])),
+ m_pVertsActive
+ ),
+ RecvPropFloat( RECVINFO(m_flFrontDistance) ),
+ RecvPropFloat( RECVINFO(m_flShieldTheta) ),
+ RecvPropFloat( RECVINFO(m_flShieldPhi) ),
+ RecvPropFloat( RECVINFO(m_flSpringConstant) ),
+ RecvPropQAngles( RECVINFO( m_angLockedAngles ) ),
+ RecvPropInt (RECVINFO(m_nAttachmentIndex) ),
+#endif
+
+END_NETWORK_TABLE()
+
+
+BEGIN_PREDICTION_DATA( CShieldMobile )
+
+ DEFINE_PRED_FIELD( m_ShieldState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flFrontDistance, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_angLockedAngles, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_TYPEDESCRIPTION( m_ShieldEffect, CShieldEffect ),
+ DEFINE_FIELD( m_nNextThinkTick, FIELD_INTEGER ),
+ DEFINE_FIELD( m_tmpAngLockedAngles, FIELD_VECTOR ),
+
+ // FIXME: How can I make this work now that I have an embedded collision property?
+// DEFINE_PRED_FIELD( m_vecMins, FIELD_VECTOR, FTYPEDESC_INSENDTABLE | FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
+// DEFINE_PRED_FIELD( m_vecMaxs, FIELD_VECTOR, FTYPEDESC_INSENDTABLE | FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
+
+#ifdef CLIENT_DLL
+ DEFINE_PRED_FIELD( m_vecNetworkOrigin, FIELD_VECTOR, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
+ DEFINE_PRED_FIELD( m_angNetworkAngles, FIELD_VECTOR, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
+#endif
+
+END_PREDICTION_DATA()
+
+
+
+//-----------------------------------------------------------------------------
+// CShieldMobileActiveVertList functions
+//-----------------------------------------------------------------------------
+void CShieldMobileActiveVertList::Init( CShieldMobile *pShield )
+{
+ m_pShield = pShield;
+}
+
+
+int CShieldMobileActiveVertList::GetActiveVertState( int iVert )
+{
+ return m_pShield->m_pVertsActive[iVert>>3] & (1 << (iVert & 7));
+}
+
+
+void CShieldMobileActiveVertList::SetActiveVertState( int iVert, int bOn )
+{
+ unsigned char val;
+ if ( bOn )
+ val = m_pShield->m_pVertsActive[iVert>>3] | (1 << (iVert & 7));
+ else
+ val = m_pShield->m_pVertsActive[iVert>>3] & ~(1 << (iVert & 7));
+
+ m_pShield->m_pVertsActive.Set( iVert>>3, val );
+}
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+CShieldMobile::CShieldMobile()
+{
+ SetPredictionEligible( true );
+ m_VertList.Init( this );
+ m_flFrontDistance = 0;
+ SetAngularSpringConstant( 2.0f );
+ SetThetaPhi( SHIELD_INITIAL_THETA, SHIELD_INITIAL_PHI );
+ m_nAttachmentIndex = 0;
+
+#ifdef CLIENT_DLL
+ InitShield( SHIELD_NUM_HORIZONTAL_POINTS, SHIELD_NUM_VERTICAL_POINTS, NUM_SUBDIVISIONS );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldMobile::Precache( void )
+{
+ m_ShieldEffect.SetActiveVertexList( &m_VertList );
+ m_ShieldEffect.SetCollisionGroup( TFCOLLISION_GROUP_SHIELD );
+ BaseClass::Precache();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldMobile::Spawn( void )
+{
+ Precache();
+ BaseClass::Spawn();
+ AddSolidFlags( FSOLID_FORCE_WORLD_ALIGNED );
+
+// Assert( GetOwnerEntity() );
+ m_angLockedAngles.Set( vec3_angle );
+ m_tmpAngLockedAngles = vec3_angle;
+ m_ShieldEffect.Spawn(GetAbsOrigin(), GetAbsAngles());
+ m_ShieldState = 0;
+ SetAlwaysOrient( true );
+
+ // All movement occurs during think
+ SetMoveType( MOVETYPE_NONE );
+
+ SetThink( ShieldThink );
+ SetNextThink( gpGlobals->curtime + 0.01f );
+
+#ifdef CLIENT_DLL
+ // This goofiness is required so that non-predicted entities work
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldMobile::SetAlwaysOrient( bool bOrient )
+{
+ if (bOrient)
+ {
+ m_ShieldState.Set( m_ShieldState.Get() | SHIELD_ORIENT_TO_OWNER );
+ }
+ else
+ {
+ m_angLockedAngles.Set( m_tmpAngLockedAngles );
+ m_ShieldState.Set( m_ShieldState.Get() & (~SHIELD_ORIENT_TO_OWNER) );
+ }
+}
+
+int CShieldMobile::Width()
+{
+ return SHIELD_NUM_HORIZONTAL_POINTS;
+}
+
+int CShieldMobile::Height()
+{
+ return SHIELD_NUM_VERTICAL_POINTS;
+}
+
+const Vector& CShieldMobile::GetPoint( int x, int y )
+{
+ return m_ShieldEffect.GetPoint( x, y );
+}
+
+
+void CShieldMobile::SetAttachmentIndex( int nAttachmentIndex )
+{
+ m_nAttachmentIndex = nAttachmentIndex;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the render bounds
+//-----------------------------------------------------------------------------
+void CShieldMobile::GetRenderBounds( Vector& mins, Vector& maxs )
+{
+ mins = m_ShieldEffect.GetRenderMins();
+ maxs = m_ShieldEffect.GetRenderMaxs();
+}
+
+//-----------------------------------------------------------------------------
+// Return true if the panel is active
+//-----------------------------------------------------------------------------
+bool CShieldMobile::IsPanelActive( int x, int y )
+{
+ return m_ShieldEffect.IsPanelActive(x, y);
+}
+
+
+//-----------------------------------------------------------------------------
+// Called when the shield is EMPed
+//-----------------------------------------------------------------------------
+void CShieldMobile::SetEMPed( bool isEmped )
+{
+ CShield::SetEMPed(isEmped);
+ if (IsEMPed())
+ m_ShieldState |= SHIELD_MOBILE_EMP;
+ else
+ m_ShieldState &= ~SHIELD_MOBILE_EMP;
+}
+
+
+//-----------------------------------------------------------------------------
+// Set the shield angles
+//-----------------------------------------------------------------------------
+void CShieldMobile::SetCenterAngles( const QAngle& angles )
+{
+ // The tmp ang locked angles is simply there to prevent unnecessary network traffic
+ m_tmpAngLockedAngles = angles;
+ if ( ( m_ShieldState.Get() & SHIELD_ORIENT_TO_OWNER ) == 0 )
+ {
+ m_angLockedAngles.Set( angles );
+ }
+}
+
+bool CShieldMobile::IsAlwaysOrienting( )
+{
+ return ( m_ShieldState.Get() & SHIELD_ORIENT_TO_OWNER ) != 0;
+}
+
+void CShieldMobile::SetAngularSpringConstant( float flConstant )
+{
+ m_flSpringConstant = flConstant;
+ m_ShieldEffect.SetAngularSpringConstant( flConstant );
+}
+
+void CShieldMobile::SetFrontDistance( float flDistance )
+{
+ m_flFrontDistance = flDistance;
+}
+
+void CShieldMobile::SetThetaPhi( float flTheta, float flPhi )
+{
+ // This sets the bounds of the shield; how tall + wide is it?
+ m_flShieldTheta = flTheta;
+ m_flShieldPhi = flPhi;
+ m_ShieldEffect.SetThetaPhi(flTheta, flPhi);
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the shield bounding box
+//-----------------------------------------------------------------------------
+void CShieldMobile::ComputeBoundingBox( void )
+{
+ Vector mins, maxs;
+ m_ShieldEffect.ComputeBounds(mins, maxs);
+ SetCollisionBounds( mins, maxs );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute world axis-aligned bounding box
+//-----------------------------------------------------------------------------
+void CShieldMobile::ComputeWorldSpaceSurroundingBox( Vector *pWorldMins, Vector *pWorldMaxs )
+{
+ // We don't use USE_SPECIFIED_BOUNDS because that would generate a ton of network traffic
+ VectorAdd( CollisionProp()->GetCollisionOrigin(), CollisionProp()->OBBMins(), *pWorldMins );
+ VectorAdd( CollisionProp()->GetCollisionOrigin(), CollisionProp()->OBBMaxs(), *pWorldMaxs );
+}
+
+
+//-----------------------------------------------------------------------------
+// Determines shield obstructions
+//-----------------------------------------------------------------------------
+void CShieldMobile::DetermineObstructions( )
+{
+ m_ShieldEffect.ComputeVertexActivity();
+}
+
+
+//-----------------------------------------------------------------------------
+// Called by the enumerator call in ShieldThink
+//-----------------------------------------------------------------------------
+bool CShieldMobile::EnumEntity( IHandleEntity *pHandleEntity )
+{
+#ifdef CLIENT_DLL
+ CBaseEntity *pOther = cl_entitylist->GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() );
+#else
+ CBaseEntity *pOther = gEntList.GetBaseEntity( pHandleEntity->GetRefEHandle() );
+#endif
+
+ if (!pOther)
+ return true;
+
+ // Blow off non-solid things
+ if ( !::IsSolid(pOther->GetSolid(), pOther->GetSolidFlags()) )
+ return true;
+
+ // No model, blow it off
+ if ( !pOther->GetModelIndex() )
+ return true;
+
+ // Blow off point-sized things....
+ if ( pOther->IsPointSized() )
+ return true;
+
+ // Don't bother if we shouldn't be colliding with this guy...
+ if (!m_pEnumCtx->m_Filter.ShouldHitEntity( pOther, MASK_SOLID ))
+ return true;
+
+ // The shield is in its final position, so we're gonna have to determine the
+ // point of collision by working in the space of the final position....
+ // We do this by moving the obstruction by the relative movement amount...
+ Vector vecObsMins, vecObsMaxs, vecObsCenter;
+ pOther->CollisionProp()->WorldSpaceAABB( &vecObsMins, &vecObsMaxs );
+ vecObsCenter = (vecObsMins + vecObsMaxs) * 0.5f;
+ vecObsMins -= vecObsCenter;
+ vecObsMaxs -= vecObsCenter;
+
+ Vector vecStart, vecEnd;
+ VectorAdd( vecObsCenter, m_pEnumCtx->m_vecStartDelta, vecStart );
+ VectorAdd( vecStart, m_pEnumCtx->m_vecEndDelta, vecEnd );
+
+
+ Ray_t ray;
+ ray.Init( vecStart, vecEnd, vecObsMins, vecObsMaxs );
+
+ trace_t tr;
+ if (TestCollision( ray, pOther->PhysicsSolidMaskForEntity(), tr ))
+ {
+ // Ok, we got a collision. Let's indicate it happened...
+ // At the moment, we'll report the collision point as being on the
+ // surface of the shield in its final position, which is kind of bogus...
+ pOther->PhysicsImpact( this, tr );
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Update the shield position:
+//-----------------------------------------------------------------------------
+void CShieldMobile::SimulateShield( void )
+{
+ CBaseEntity *owner = GetOwnerEntity();
+ Vector origin;
+ if ( owner )
+ {
+ if ( m_ShieldState & SHIELD_ORIENT_TO_OWNER )
+ {
+ if ( owner->IsPlayer() )
+ {
+ m_ShieldEffect.SetDesiredAngles( owner->EyeAngles() );
+ }
+ else
+ {
+ m_ShieldEffect.SetDesiredAngles( owner->GetAbsAngles() );
+ }
+ }
+ else
+ {
+ m_ShieldEffect.SetDesiredAngles( m_angLockedAngles );
+ }
+
+ if ( m_nAttachmentIndex == 0 )
+ {
+ origin = owner->EyePosition();
+ }
+ else
+ {
+ QAngle angles;
+ CBaseAnimating *pAnim = dynamic_cast<CBaseAnimating*>( owner );
+ if (pAnim)
+ {
+ pAnim->GetAttachment( m_nAttachmentIndex, origin, angles );
+ }
+ else
+ {
+ origin = owner->EyePosition();
+ }
+ }
+
+ if ( m_flFrontDistance )
+ {
+ Vector vForward;
+ AngleVectors( m_ShieldEffect.GetDesiredAngles(), &vForward );
+ origin += vForward * m_flFrontDistance;
+ }
+ }
+ else
+ {
+ Assert( 0 );
+ origin = vec3_origin;
+ }
+
+ // We pretty much always need to recompute this
+ CollisionProp()->MarkSurroundingBoundsDirty();
+
+ Vector vecOldOrigin = m_ShieldEffect.GetCurrentPosition();
+
+ Vector vecDelta;
+ VectorSubtract( origin, vecOldOrigin, vecDelta );
+
+ float flMaxDist = 100 + m_flFrontDistance;
+ if (vecDelta.LengthSqr() > flMaxDist * flMaxDist )
+ {
+ OnTeleported();
+ return;
+ }
+
+ m_ShieldEffect.SetDesiredOrigin( origin );
+ m_ShieldEffect.Simulate(gpGlobals->frametime);
+ DetermineObstructions();
+ SetAbsOrigin( m_ShieldEffect.GetCurrentPosition() );
+ SetAbsAngles( m_ShieldEffect.GetCurrentAngles() );
+
+#ifdef CLIENT_DLL
+ // Necessary because we exclude the network origin
+ SetNetworkOrigin( m_ShieldEffect.GetCurrentPosition() );
+ SetNetworkAngles( m_ShieldEffect.GetCurrentAngles() );
+#endif
+
+ // Compute a composite bounding box surrounding the initial + new positions..
+ Vector vecCompositeMins = WorldAlignMins() + vecOldOrigin;
+ Vector vecCompositeMaxs = WorldAlignMaxs() + vecOldOrigin;
+
+ ComputeBoundingBox();
+
+ // Sweep the shield through the world + touch things it hits...
+ SweepContext_t ctx( this, GetCollisionGroup() );
+ VectorSubtract( GetAbsOrigin(), vecOldOrigin, ctx.m_vecStartDelta );
+
+ if (ctx.m_vecStartDelta != vec3_origin)
+ {
+ // FIXME: Brutal hack; needed because IntersectRayWithTriangle misses stuff
+ // especially with short rays; I'm not sure what to do about this.
+ // This basically simulates a shield thickness of 15 units
+ ctx.m_vecEndDelta = ctx.m_vecStartDelta;
+ VectorNormalize( ctx.m_vecEndDelta );
+ ctx.m_vecEndDelta *= -15.0f;
+
+ Vector vecNewMins = WorldAlignMins() + GetAbsOrigin();
+ Vector vecNewMaxs = WorldAlignMaxs() + GetAbsOrigin();
+ VectorMin( vecCompositeMins, vecNewMins, vecCompositeMins );
+ VectorMax( vecCompositeMaxs, vecNewMaxs, vecCompositeMaxs );
+
+ m_pEnumCtx = &ctx;
+ enginetrace->EnumerateEntities( vecCompositeMins, vecCompositeMaxs, this );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Update the shield position:
+//-----------------------------------------------------------------------------
+void CShieldMobile::ShieldThink( void )
+{
+ SimulateShield();
+
+#ifdef CLIENT_DLL
+ m_ShieldEffect.ComputeControlPoints();
+ m_ShieldEffect.ComputePanelActivity();
+#endif
+
+ SetNextThink( gpGlobals->curtime + 0.01f );
+}
+
+void CShieldMobile::ClientThink()
+{
+#ifdef CLIENT_DLL
+ if ( GetPredictable() )
+ return;
+
+ SimulateShield();
+ m_ShieldEffect.ComputeControlPoints();
+ m_ShieldEffect.ComputePanelActivity();
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Teleport!
+//-----------------------------------------------------------------------------
+void CShieldMobile::OnTeleported( )
+{
+ CBaseEntity *owner = GetOwnerEntity();
+ if (!owner)
+ return;
+
+ m_ShieldEffect.SetCurrentAngles( owner->GetAbsAngles() );
+
+ Vector origin;
+ origin = owner->EyePosition();
+ if ( m_flFrontDistance )
+ {
+ Vector vForward;
+ AngleVectors( m_ShieldEffect.GetCurrentAngles(), &vForward );
+ origin += vForward * m_flFrontDistance;
+ }
+
+ m_ShieldEffect.SetCurrentPosition( origin );
+}
+
+
+
+#ifdef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Get this after the data changes
+//-----------------------------------------------------------------------------
+void CShieldMobile::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+ m_ShieldEffect.SetThetaPhi( m_flShieldTheta, m_flShieldPhi );
+ m_ShieldEffect.SetAngularSpringConstant( m_flSpringConstant );
+}
+
+
+//-----------------------------------------------------------------------------
+// A little pre-render processing
+//-----------------------------------------------------------------------------
+void C_ShieldMobile::PreRender( )
+{
+ if (m_ShieldState & SHIELD_MOBILE_EMP)
+ {
+ // Decay fade if we've been EMPed or if we're inactive
+ if (m_FadeValue > 0.0f)
+ {
+ m_FadeValue -= gpGlobals->frametime / SHIELD_EMP_FADE_TIME;
+ if (m_FadeValue < 0.0f)
+ {
+ m_FadeValue = 0.0f;
+
+ // Reset the shield to un-wobbled state
+ m_ShieldEffect.ComputeControlPoints();
+ }
+ else
+ {
+ Vector dir;
+ AngleVectors( m_ShieldEffect.GetCurrentAngles(), & dir, 0, 0 );
+
+ // Futz with the control points if we've been EMPed
+ for (int i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i)
+ {
+ // Get the direction for the point
+ float factor = -EMP_WAVE_AMPLITUDE * sin( i * M_PI * 0.5f + gpGlobals->curtime * M_PI / SHIELD_EMP_WOBBLE_TIME );
+ m_ShieldEffect.GetPoint(i) += dir * factor;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Fade back in, no longer EMPed
+ if (m_FadeValue < 1.0f)
+ {
+ m_FadeValue += gpGlobals->frametime / SHIELD_EMP_FADE_TIME;
+ if (m_FadeValue >= 1.0f)
+ {
+ m_FadeValue = 1.0f;
+ }
+ }
+ }
+}
+
+void CShieldMobile::AddEntity( )
+{
+ BaseClass::AddEntity( );
+ PreRender();
+}
+
+
+//-----------------------------------------------------------------------------
+// Bounds computation
+//-----------------------------------------------------------------------------
+void CShieldMobile::GetBounds( Vector& mins, Vector& maxs )
+{
+ m_ShieldEffect.ComputeBounds( mins, maxs );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets at the control point data; who knows how it was made?
+//-----------------------------------------------------------------------------
+void CShieldMobile::GetShieldData( const Vector** ppVerts, float* pOpacity, float* pBlend )
+{
+ for ( int i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i )
+ {
+ ppVerts[i] = &m_ShieldEffect.GetControlPoint(i);
+
+ if ( m_pVertsActive[i >> 3] & (1 << (i & 0x7)) )
+ {
+ pOpacity[i] = m_ShieldEffect.ComputeOpacity( *ppVerts[i], GetAbsOrigin() );
+ pBlend[i] = 1.0f;
+ }
+ else
+ {
+ pOpacity[i] = 192.0f;
+ pBlend[i] = 0.0f;
+ }
+ }
+}
+
+
+#endif // CLIENT_DLL
+
+
+#ifndef CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a mobile version of the shield
+//-----------------------------------------------------------------------------
+CShield *CreateMobileShield( CBaseEntity *owner, float flFrontDistance )
+{
+ CShieldMobile *pShield = (CShieldMobile*)CreateEntityByName("shield_mobile");
+
+ pShield->SetOwnerEntity( owner );
+ pShield->SetLocalAngles( owner->GetAbsAngles() );
+ pShield->SetFrontDistance( flFrontDistance );
+
+ // Start it in the right place
+ Vector vForward;
+ AngleVectors( owner->GetAbsAngles(), &vForward );
+ Vector vecOrigin = owner->EyePosition() + (vForward * flFrontDistance);
+ UTIL_SetOrigin( pShield, vecOrigin );
+
+ pShield->ChangeTeam( owner->GetTeamNumber() );
+ pShield->SetupRecharge( shield_mobile_power.GetFloat(), shield_mobile_recharge_delay.GetFloat(), shield_mobile_recharge_amount.GetFloat(), shield_mobile_recharge_time.GetFloat() );
+ pShield->Spawn();
+
+ return pShield;
+}
+
+#endif
+
diff --git a/game/shared/tf2/tf_shieldshared.cpp b/game/shared/tf2/tf_shieldshared.cpp
new file mode 100644
index 0000000..a7370ce
--- /dev/null
+++ b/game/shared/tf2/tf_shieldshared.cpp
@@ -0,0 +1,579 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The Escort's Shield weapon effect
+//
+// $Workfile: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "tf_shieldshared.h"
+#include "edict.h"
+#include "mathlib/vmatrix.h"
+#include "engine/IEngineTrace.h"
+
+#ifdef CLIENT_DLL
+#include "cdll_client_int.h"
+#else
+#include "gameinterface.h"
+#endif
+
+
+
+BEGIN_PREDICTION_DATA_NO_BASE( CShieldEffect )
+
+ DEFINE_FIELD( m_TestPoint, FIELD_INTEGER ),
+ DEFINE_FIELD( m_Position, FIELD_VECTOR ),
+ DEFINE_FIELD( m_Velocity, FIELD_VECTOR ),
+ DEFINE_FIELD( m_CurrentAngles, FIELD_VECTOR ),
+ DEFINE_FIELD( m_Theta, FIELD_FLOAT ),
+ DEFINE_FIELD( m_Phi, FIELD_FLOAT ),
+ DEFINE_FIELD( m_ThetaVelocity, FIELD_FLOAT ),
+ DEFINE_FIELD( m_PhiVelocity, FIELD_FLOAT ),
+ DEFINE_FIELD( m_vecDesiredOrigin, FIELD_VECTOR ),
+ DEFINE_FIELD( m_angDesiredAngles, FIELD_VECTOR ),
+ DEFINE_FIELD( m_ShieldTheta, FIELD_FLOAT ),
+ DEFINE_FIELD( m_ShieldPhi, FIELD_FLOAT ),
+
+END_PREDICTION_DATA()
+
+
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+CShieldEffect::CShieldEffect( )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// compute rest positions of the springs
+//-----------------------------------------------------------------------------
+void CShieldEffect::ComputeRestPositions()
+{
+ int i;
+
+ m_vecRenderMins.Init( FLT_MAX, FLT_MAX, FLT_MAX );
+ m_vecRenderMaxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
+
+ // Set the initial directions and distances (in shield space)...
+ for ( i = 0; i < SHIELD_NUM_VERTICAL_POINTS; ++i)
+ {
+ // Choose phi centered at pi/2
+ float phi = (M_PI - m_ShieldPhi) * 0.5f + m_ShieldPhi *
+ (float)i / (float)(SHIELD_NUM_VERTICAL_POINTS - 1);
+
+ for (int j = 0; j < SHIELD_NUM_HORIZONTAL_POINTS; ++j)
+ {
+ // Choose theta centered at pi/2 also (y, or forward axis)
+ float theta = (M_PI - m_ShieldTheta) * 0.5f + m_ShieldTheta *
+ (float)j / (float)(SHIELD_NUM_HORIZONTAL_POINTS - 1);
+
+ int idx = i * SHIELD_NUM_HORIZONTAL_POINTS + j;
+
+ m_pFixedDirection[idx].x = cos(theta) * sin(phi);
+ m_pFixedDirection[idx].y = sin(theta) * sin(phi);
+ m_pFixedDirection[idx].z = cos(phi);
+
+ m_pFixedDirection[idx] *= m_RestLength;
+
+ VectorMin( m_vecRenderMins, m_pFixedDirection[idx], m_vecRenderMins );
+ VectorMax( m_vecRenderMaxs, m_pFixedDirection[idx], m_vecRenderMaxs );
+ }
+ }
+
+ // Compute box for fake volume testing
+ Vector dist = m_pFixedDirection[0] - m_pFixedDirection[1];
+ float l = dist.Length(); // * m_RestLength;
+ SetShieldPanelSize( Vector( -l * 0.25f, -l * 0.25f, -l * 0.25f),
+ Vector( l * 0.25f, l * 0.25f, l * 0.25f) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets orientation + position
+//-----------------------------------------------------------------------------
+void CShieldEffect::SetDesiredOrigin( const Vector& origin )
+{
+ VectorCopy( origin, m_vecDesiredOrigin );
+}
+
+void CShieldEffect::SetDesiredAngles( const QAngle& angles )
+{
+ VectorCopy( angles, m_angDesiredAngles );
+}
+
+const QAngle& CShieldEffect::GetDesiredAngles() const
+{
+ return m_angDesiredAngles;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets a point...
+//-----------------------------------------------------------------------------
+const Vector& CShieldEffect::GetPoint( int x, int y ) const
+{
+ return m_pControlPoint[ x + y * SHIELD_NUM_HORIZONTAL_POINTS ];
+}
+
+const Vector& CShieldEffect::GetPoint( int i ) const
+{
+ return m_pControlPoint[ i ];
+}
+
+Vector& CShieldEffect::GetPoint( int i )
+{
+ return m_pControlPoint[ i ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the collision group
+//-----------------------------------------------------------------------------
+void CShieldEffect::SetCollisionGroup( int group )
+{
+ m_CollisionGroup = group;
+}
+
+
+//-----------------------------------------------------------------------------
+// Hooks in active bits...
+//-----------------------------------------------------------------------------
+void CShieldEffect::SetActiveVertexList( IActiveVertList *pActiveVerts )
+{
+ m_pActiveVerts = pActiveVerts;
+
+ // No points are visible initially
+ for ( int i=0; i < SHIELD_VERTEX_BYTES*8; i++ )
+ m_pActiveVerts->SetActiveVertState( i, 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Is a particular vertex active?
+//-----------------------------------------------------------------------------
+bool CShieldEffect::IsVertexActive( int x, int y ) const
+{
+ if ((x < 0) || (y < 0) || (x >= SHIELD_NUM_HORIZONTAL_POINTS) ||
+ (y >= SHIELD_NUM_VERTICAL_POINTS))
+ {
+ return false;
+ }
+
+ int idx = x + (SHIELD_NUM_HORIZONTAL_POINTS) * y;
+ return m_pActiveVerts->GetActiveVertState( idx ) != 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Is a particular panel active?
+//-----------------------------------------------------------------------------
+bool CShieldEffect::IsPanelActive( int x, int y ) const
+{
+ if ((x < 0) || (y < 0) || (x >= SHIELD_HORIZONTAL_PANEL_COUNT) ||
+ (y >= SHIELD_VERTICAL_PANEL_COUNT))
+ {
+ return false;
+ }
+
+ int idx = x + (SHIELD_HORIZONTAL_PANEL_COUNT) * y;
+ return m_pActivePanels[idx];
+}
+
+
+//-----------------------------------------------------------------------------
+// Recompute whether the panels are active or not
+//-----------------------------------------------------------------------------
+void CShieldEffect::ComputePanelActivity()
+{
+ // Check neighbors to see how many squares we've got
+ for ( int i = 0; i < SHIELD_NUM_HORIZONTAL_POINTS - 1; ++i)
+ {
+ for ( int j = 0; j < SHIELD_NUM_VERTICAL_POINTS - 1; ++j)
+ {
+ int idx = i + j * (SHIELD_NUM_HORIZONTAL_POINTS - 1);
+
+ // Test the neighbors
+ m_pActivePanels[idx] =
+ IsVertexActive( i, j ) ||
+ IsVertexActive( i+1, j ) ||
+ IsVertexActive( i, j+1 ) ||
+ IsVertexActive( i+1, j+1 );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute vertex activity
+//-----------------------------------------------------------------------------
+
+enum
+{
+ SHIELD_TESTS_PER_FRAME = 4
+};
+
+
+void CShieldEffect::ComputeVertexActivity()
+{
+ int i;
+ for ( i = 0; i < SHIELD_TESTS_PER_FRAME; ++i )
+ {
+ // Visit points in random order...
+ int pt = m_PointList[m_TestPoint];
+
+ // Collision test...
+ // Check a line that goes farther out than our current point...
+ // This will let us check for resting contact
+ trace_t tr;
+ CTraceFilterWorldOnly traceFilter;
+ UTIL_TraceHull( m_Position, m_pControlPoint[pt],
+ m_PanelBoxMin, m_PanelBoxMax, MASK_SOLID_BRUSHONLY, &traceFilter, &tr );
+ bool isActive = (!tr.allsolid) && ( (tr.fraction - 1.0f) >= 0.0f );
+
+ m_pActiveVerts->SetActiveVertState( pt, isActive );
+
+ if (++m_TestPoint >= SHIELD_NUM_CONTROL_POINTS)
+ m_TestPoint = 0;
+
+ }
+
+ ComputePanelActivity();
+}
+
+
+//-----------------------------------------------------------------------------
+// bounding box for collision
+//-----------------------------------------------------------------------------
+void CShieldEffect::SetShieldPanelSize( Vector& mins, Vector& maxs )
+{
+ m_PanelBoxMin = mins;
+ m_PanelBoxMax = maxs;
+}
+
+
+//-----------------------------------------------------------------------------
+// bounding box for collision
+//-----------------------------------------------------------------------------
+void CShieldEffect::ComputeBounds( Vector& mins, Vector& maxs )
+{
+ VectorCopy( m_pControlPoint[0], mins );
+ VectorCopy( m_pControlPoint[0], maxs );
+
+ for (int i = 1; i < SHIELD_NUM_CONTROL_POINTS; ++i)
+ {
+ VectorMin( mins, m_pControlPoint[i], mins );
+ VectorMax( maxs, m_pControlPoint[i], maxs );
+ }
+
+ // Bounds are in local coords
+ mins -= m_Position;
+ maxs -= m_Position;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldEffect::Precache( void )
+{
+ m_RestLength = 200.0;
+
+ m_SpringConstant = 30.0f;
+ m_DampConstant = 4.0f;
+ m_ViscousDrag = 4.0f;
+ m_Mass = 1.0f;
+
+ m_AngularSpringConstant = 2.0f;
+ m_AngularViscousDrag = 4.0f;
+
+ SetThetaPhi( SHIELD_INITIAL_THETA, SHIELD_INITIAL_PHI );
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute orientation matrix:
+//-----------------------------------------------------------------------------
+void CShieldEffect::ComputeOrientationMatrix()
+{
+ // Generate the orientation matrix from theta and phi...
+ // X = forward direction, Y - left direction
+ Vector forward, left, up;
+ forward.x = cos(m_Theta) * sin(m_Phi);
+ forward.y = sin(m_Theta) * sin(m_Phi);
+ forward.z = cos(m_Phi);
+
+ left.x = -forward.y;
+ left.y = forward.x;
+ left.z = 0;
+
+ if ( VectorNormalize(left) == 0.0f )
+ left.Init( 0.0f, 1.0f, 0.0f );
+
+ CrossProduct( forward, left, up );
+
+ m_Orientation.SetBasisVectors( forward, left, up );
+
+ // Turn the current matrix into angles...
+ MatrixToAngles( m_Orientation, m_CurrentAngles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CShieldEffect::Spawn( const Vector& currentPosition, const QAngle& currentAngles )
+{
+ Precache();
+
+ VectorCopy( currentPosition, m_Position );
+ m_Velocity.Init();
+
+ Vector forward;
+ AngleVectors( currentAngles, &forward, 0, 0 );
+ m_Phi = acos( forward.z );
+ m_Theta = atan2( forward.y, forward.x );
+ m_PhiVelocity = 0.0f;
+ m_ThetaVelocity = 0.0f;
+ ComputeOrientationMatrix();
+ VectorCopy( currentAngles, m_CurrentAngles );
+ VectorCopy( currentAngles, m_angDesiredAngles );
+
+ // No points are visible initially
+ memset( m_pActivePanels, 0, SHIELD_PANELS_COUNT );
+
+ m_TestPoint = 0;
+
+ // Choose random order to visit shield verts
+ int i;
+ for ( i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i )
+ {
+ m_PointList[i] = i;
+ }
+
+ for ( i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i )
+ {
+ int j = rand() % SHIELD_NUM_CONTROL_POINTS;
+ swap( m_PointList[i], m_PointList[j] );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the opacity....
+//-----------------------------------------------------------------------------
+float CShieldEffect::ComputeOpacity( const Vector& pt, const Vector& center ) const
+{
+ float dist = pt.DistTo( center ) / m_RestLength;
+ if (dist > 1.0)
+ dist = 1.0f;
+ return 32 + (1.0 - dist) * 192;
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes control points
+//-----------------------------------------------------------------------------
+void CShieldEffect::ComputeControlPoints()
+{
+ Vector forward, right, up;
+ AngleVectors(m_CurrentAngles, &forward, &right, &up);
+
+ for ( int i = 0; i < SHIELD_NUM_CONTROL_POINTS; ++i )
+ {
+ // Compute the world space position...
+ VectorCopy( m_Position, m_pControlPoint[i] );
+ m_pControlPoint[i] += right * m_pFixedDirection[i].x;
+ m_pControlPoint[i] += up * m_pFixedDirection[i].z;
+ m_pControlPoint[i] += forward * m_pFixedDirection[i].y;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets the frustum size
+//-----------------------------------------------------------------------------
+void CShieldEffect::GetPanelSize( Vector& mins, Vector& maxs ) const
+{
+ VectorCopy( m_PanelBoxMin, mins );
+ VectorCopy( m_PanelBoxMax, maxs );
+}
+
+
+void CShieldEffect::SetAngularSpringConstant( float flConstant )
+{
+ m_AngularSpringConstant = flConstant;
+}
+
+
+//-----------------------------------------------------------------------------
+// Set the shield theta & phi
+//-----------------------------------------------------------------------------
+void CShieldEffect::SetThetaPhi( float flTheta, float flPhi )
+{
+ m_ShieldTheta = M_PI * flTheta / 180.0f;
+ m_ShieldPhi = M_PI * flPhi / 180.0f;
+
+ // Computes the rest positions
+ ComputeRestPositions();
+}
+
+
+//-----------------------------------------------------------------------------
+// The current position (computed by Simulate on the server)
+//-----------------------------------------------------------------------------
+const Vector& CShieldEffect::GetCurrentPosition()
+{
+ return m_Position;
+}
+
+void CShieldEffect::SetCurrentPosition( const Vector& pos )
+{
+ m_Position = pos;
+}
+
+void CShieldEffect::SetCurrentAngles( const QAngle& angles )
+{
+ VectorCopy( angles, m_CurrentAngles );
+}
+
+
+//-----------------------------------------------------------------------------
+// Simulate the center of mass
+//-----------------------------------------------------------------------------
+void CShieldEffect::SimulateTranslation( float dt )
+{
+ // Hook's law for a damped spring:
+ // got two particles, a and b with positions xa and xb and velocities va and vb
+ // and l = xa - xb
+ // fa = -( ks * (|l| - r) + kd * (va - vb) dot (l) / |l|) * l/|l|
+ Vector dx, force;
+
+ // Case where we're connected to a control point
+ dx = m_Position - m_vecDesiredOrigin;
+
+ // rest condition
+ float length = dx.Length();
+ float speedSq = m_Velocity.LengthSqr();
+ if ((length < 1e-3) && (speedSq < 1e-6))
+ return;
+
+ // Compute force
+ if (length > 1e-3)
+ dx /= length;
+ else
+ dx.Init( 0, 0, 0 );
+
+ float springfactor = m_SpringConstant * length;
+ float dampfactor = m_DampConstant * DotProduct( m_Velocity, dx );
+ force = dx * -( springfactor + dampfactor );
+
+ assert( force.IsValid( ) );
+ Vector drag = m_Velocity * m_ViscousDrag;
+ force -= drag;
+
+ // Update position and velocity
+ m_Position += m_Velocity * dt;
+ m_Velocity += force * dt / m_Mass;
+
+ assert( m_Velocity.IsValid( ) );
+
+ // clamp for stability
+ if (speedSq > 1e6)
+ {
+ m_Velocity *= 1e3 / sqrt(speedSq);
+ }
+}
+
+
+void CShieldEffect::SimulateRotation( float dt, const Vector& forward )
+{
+ // Here's a torsional spring for the angular component...
+ // A little tricky: We need to actually think about 2 torsional springs,
+ // one in thetha (x-y plane), and one in phi (z-plane)
+
+ float phi2 = acos( forward.z );
+ float dPhi = m_Phi - phi2;
+
+ float theta2 = atan2( forward.y, forward.x );
+ float dTheta = (m_Theta - theta2);
+ if (dTheta > M_PI)
+ dTheta -= 2 * M_PI;
+ else if (dTheta < -M_PI)
+ dTheta += 2 * M_PI;
+
+ // rest condition...
+ if ((fabs(dTheta) < 1e-3) && (fabs(m_ThetaVelocity) < 1e-6) &&
+ (fabs(dPhi) < 1e-3) && (fabs(m_PhiVelocity) < 1e-6))
+ {
+ return;
+ }
+
+ float springfactor = m_AngularSpringConstant * dTheta;
+ float torqueTheta = -springfactor; // + dampfactor);
+ torqueTheta -= m_ThetaVelocity * m_AngularViscousDrag;
+
+ springfactor = m_AngularSpringConstant * dPhi;
+ float torqueTPhi = -springfactor; // + dampfactor);
+ torqueTPhi -= m_PhiVelocity * m_AngularViscousDrag;
+
+ // Update position and velocity
+ m_Theta += m_ThetaVelocity * dt;
+ m_ThetaVelocity += torqueTheta * dt;
+ m_Phi += m_PhiVelocity * dt;
+ m_PhiVelocity += torqueTPhi * dt;
+
+ // clamp for stability
+ if (fabs(m_ThetaVelocity) > 1e2)
+ {
+ m_ThetaVelocity *= 1e2 / m_ThetaVelocity;
+ }
+ if (fabs(m_PhiVelocity) > 1e2)
+ {
+ m_PhiVelocity *= 1e2 / m_PhiVelocity;
+ }
+
+ ComputeOrientationMatrix();
+}
+
+
+//-----------------------------------------------------------------------------
+// Update the shield position:
+//-----------------------------------------------------------------------------
+void CShieldEffect::Simulate( float dt )
+{
+ // We're gonna basically assume a spring connected to the center control point
+ Vector forward;
+ AngleVectors(m_angDesiredAngles, &forward, 0, 0);
+
+ // We've got two springs: a spring connected to the origin
+ // and a torsional spring connected to the view direction.
+
+ // Stiff spring, subdivide time....
+ dt /= SHIELD_TIME_SUBVISIBIONS;
+ for (int i = 0; i < SHIELD_TIME_SUBVISIBIONS; ++i)
+ {
+ SimulateTranslation( dt );
+ SimulateRotation( dt, forward );
+ }
+
+ ComputeControlPoints();
+}
+
+
+//-----------------------------------------------------------------------------
+// Determines shield obstructions
+//-----------------------------------------------------------------------------
+static inline bool IsPointValid( bool* pActivePoints, int i, int j )
+{
+ // Here's the control point we're checking
+ int idx = j * SHIELD_NUM_HORIZONTAL_POINTS + i;
+
+ return pActivePoints[idx];
+}
+
+
+
diff --git a/game/shared/tf2/tf_shieldshared.h b/game/shared/tf2/tf_shieldshared.h
new file mode 100644
index 0000000..52ed301
--- /dev/null
+++ b/game/shared/tf2/tf_shieldshared.h
@@ -0,0 +1,262 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_SHIELD_SHARED_H
+#define TF_SHIELD_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "mathlib/mathlib.h"
+#include "mathlib/vector.h"
+#include "mathlib/vmatrix.h"
+#include "utlvector.h"
+#include "SheetSimulator.h"
+#include "predictable_entity.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Shield (mobile version)
+//-----------------------------------------------------------------------------
+enum
+{
+ SHIELD_NUM_HORIZONTAL_POINTS = 8,
+ SHIELD_NUM_VERTICAL_POINTS = 8,
+ SHIELD_NUM_CONTROL_POINTS = SHIELD_NUM_HORIZONTAL_POINTS * SHIELD_NUM_VERTICAL_POINTS,
+ SHIELD_INITIAL_THETA = 135,
+ SHIELD_INITIAL_PHI = 90,
+ SHIELD_HORIZONTAL_PANEL_COUNT = (SHIELD_NUM_HORIZONTAL_POINTS - 1),
+ SHIELD_VERTICAL_PANEL_COUNT = (SHIELD_NUM_VERTICAL_POINTS - 1),
+ SHIELD_PANELS_COUNT = (SHIELD_HORIZONTAL_PANEL_COUNT * SHIELD_VERTICAL_PANEL_COUNT),
+ SHIELD_VERTEX_BYTES = (SHIELD_NUM_CONTROL_POINTS + 7) >> 3,
+ SHIELD_TIME_SUBVISIBIONS = 2
+};
+
+//--------------------------------------------------------------------------
+// Mobile shield state flags
+//--------------------------------------------------------------------------
+enum
+{
+ SHIELD_MOBILE_EMP = 0x1
+};
+
+
+//--------------------------------------------------------------------------
+// Shield grenade state
+//--------------------------------------------------------------------------
+enum
+{
+ SHIELD_FLAT_EMP = 0x1,
+ SHIELD_FLAT_INACTIVE = 0x2
+};
+
+enum
+{
+ SHIELD_FLAT_SHUTDOWN_TIME = 1
+};
+
+enum
+{
+ SHIELD_GRENADE_WIDTH = 150,
+ SHIELD_GRENADE_HEIGHT = 150,
+};
+
+
+#define SHIELD_DAMAGE_CHANGE_TIME 1.5f
+
+
+//-----------------------------------------------------------------------------
+// Amount of time it takes to fade the shield in or out due to EMP
+//-----------------------------------------------------------------------------
+#define SHIELD_EMP_FADE_TIME 0.7f
+
+
+//-----------------------------------------------------------------------------
+// Amount of time it takes a point to wobble when EMPed
+//-----------------------------------------------------------------------------
+#define SHIELD_EMP_WOBBLE_TIME 0.1f
+
+
+//-----------------------------------------------------------------------------
+// Methods we must install into the effect
+//-----------------------------------------------------------------------------
+class IActiveVertList
+{
+public:
+ virtual int GetActiveVertState( int iVert ) = 0;
+ virtual void SetActiveVertState( int iVert, int bOn ) = 0;
+};
+
+
+class CShieldEffect
+{
+ DECLARE_CLASS_NOBASE( CShieldEffect );
+ DECLARE_PREDICTABLE();
+
+public:
+ CShieldEffect();
+
+ void Precache();
+ void Spawn(const Vector& currentPosition, const QAngle& currentAngles);
+
+ // Sets the collision group
+ void SetCollisionGroup( int group );
+
+ // Computes the opacity....
+ float ComputeOpacity( const Vector& pt, const Vector& center ) const;
+
+ // Computes the bounds
+ void ComputeBounds( Vector& mins, Vector& maxs );
+
+ // Simulation
+ void Simulate( float dt );
+
+ // Sets desired orientation + position
+ void SetDesiredOrigin( const Vector& origin );
+ void SetDesiredAngles( const QAngle& angles );
+ const QAngle& GetDesiredAngles() const;
+
+ // Hooks in active bits...
+ void SetActiveVertexList( IActiveVertList *pActiveVerts );
+
+ // Gets a point...
+ const Vector& GetPoint( int x, int y ) const;
+ const Vector& GetPoint( int i ) const;
+ Vector& GetPoint( int i );
+
+ // Computes control points
+ void ComputeControlPoints();
+
+ // The current angles (computed by Simulate on the server)
+ const QAngle& GetCurrentAngles() const;
+ void SetCurrentAngles( const QAngle& angles);
+
+ // The current position (computed by Simulate on the server)
+ const Vector& GetCurrentPosition();
+ void SetCurrentPosition( const Vector& pos );
+
+ // Compute vertex activity
+ void ComputeVertexActivity();
+
+ // Recompute whether the panels are active or not
+ void ComputePanelActivity();
+
+ // Is a particular vertex active?
+ bool IsVertexActive( int x, int y ) const;
+
+ // Is a particular panel active?
+ bool IsPanelActive( int x, int y ) const;
+
+ // Gets a control point (for collision)
+ const Vector& GetControlPoint( int i ) const { return m_pControlPoint[i]; }
+
+ // Returns the panel size (for collision testing)
+ void GetPanelSize( Vector& mins, Vector& maxs ) const;
+
+ // Change the angular spring constant. This affects how fast the shield rotates to face the angles
+ // given in SetAngles. Higher numbers are more responsive, but if you go too high (around 40), it will
+ // jump past the specified angles and wiggle a little bit.
+ void SetAngularSpringConstant( float flConstant );
+
+ // Set the shield theta & phi
+ void SetThetaPhi( float flTheta, float flPhi );
+
+ // Returns the render bounds
+ const Vector& GetRenderMins() const;
+ const Vector& GetRenderMaxs() const;
+
+private:
+ // Simulation set up
+ void ComputeRestPositions();
+ void SetShieldPanelSize( Vector& mins, Vector& maxs );
+ void SimulateTranslation( float dt );
+ void SimulateRotation( float dt, const Vector& forward );
+ void ComputeOrientationMatrix();
+
+ float m_RestLength;
+ float m_PlaneDist;
+ float m_ShieldTheta;
+ float m_ShieldPhi;
+
+ // Spring constants
+ float m_SpringConstant;
+ float m_DampConstant;
+ float m_ViscousDrag;
+ float m_Mass;
+
+ float m_AngularSpringConstant;
+ float m_AngularViscousDrag;
+
+ // collision group
+ int m_CollisionGroup;
+
+ // Directions of the control points in shield space
+ Vector m_pFixedDirection[SHIELD_NUM_CONTROL_POINTS];
+
+ // Position of the control points in world space
+ Vector m_pControlPoint[SHIELD_NUM_CONTROL_POINTS];
+
+ // Bitfield indicating which vertices are active
+ IActiveVertList *m_pActiveVerts;
+
+ // Bitfield indicating which panels are active
+ bool m_pActivePanels[SHIELD_PANELS_COUNT];
+
+ // Which point on the shield to test next
+ int m_TestPoint;
+ int m_PointList[SHIELD_NUM_CONTROL_POINTS];
+
+ // desired position + orientation
+ Vector m_vecDesiredOrigin;
+ QAngle m_angDesiredAngles;
+
+ // collision box
+ Vector m_PanelBoxMin;
+ Vector m_PanelBoxMax;
+
+ // Render bounds (shield space)
+ Vector m_vecRenderMins;
+ Vector m_vecRenderMaxs;
+
+ // Actual center position (relative to m_Origin)
+ // + velocity (world space)
+ Vector m_Position;
+ Vector m_Velocity;
+
+ // our current orientation....
+ QAngle m_CurrentAngles;
+ VMatrix m_Orientation;
+ float m_Theta;
+ float m_Phi;
+ float m_ThetaVelocity;
+ float m_PhiVelocity;
+};
+
+
+//-----------------------------------------------------------------------------
+// Inline methods
+//-----------------------------------------------------------------------------
+inline const QAngle& CShieldEffect::GetCurrentAngles() const
+{
+ return m_CurrentAngles;
+}
+
+//-----------------------------------------------------------------------------
+// Returns the render bounds
+//-----------------------------------------------------------------------------
+inline const Vector& CShieldEffect::GetRenderMins() const
+{
+ return m_vecRenderMins;
+}
+
+inline const Vector& CShieldEffect::GetRenderMaxs() const
+{
+ return m_vecRenderMaxs;
+}
+
+
+#endif // TF_SHIELD_SHARED_H
diff --git a/game/shared/tf2/tf_tacticalmap.cpp b/game/shared/tf2/tf_tacticalmap.cpp
new file mode 100644
index 0000000..19e2d42
--- /dev/null
+++ b/game/shared/tf2/tf_tacticalmap.cpp
@@ -0,0 +1,122 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Shared stuff for the Tactical map
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+
+// Unfortunate hack.
+// Needed to cycle through the player & radar scanner entities in both the client and game dlls
+#ifdef CLIENT_DLL
+
+// Client DLL functions
+#include "c_team.h"
+#include "c_tfteam.h"
+#include "c_basetfplayer.h"
+#include "C_BaseObject.h"
+
+static inline bool IsPlayerCamoed( int iEntIndex )
+{
+ C_BaseTFPlayer* pPlayer = (C_BaseTFPlayer*)ClientEntityList().GetClientEntity(iEntIndex);
+ if (!pPlayer)
+ return false;
+
+ return pPlayer->IsCamouflaged();
+}
+
+static inline bool IsPlayerVisible( int iEntIndex )
+{
+ C_BaseTFPlayer* pPlayer = (C_BaseTFPlayer*)ClientEntityList().GetClientEntity(iEntIndex);
+ if (!pPlayer)
+ return false;
+
+ return pPlayer->GetClass() != TFCLASS_UNDECIDED;
+}
+
+static inline bool IsEntityAnObject( int iEntIndex )
+{
+ IClientNetworkable *pEnt = ClientEntityList().GetClientEntity(iEntIndex);
+ return dynamic_cast<C_BaseObject*>(pEnt) != 0;
+}
+
+#else
+
+// Game DLL functions
+#include "team.h"
+#include "tf_team.h"
+#include "tf_player.h"
+
+static inline bool IsPlayerCamoed( int iEntIndex )
+{
+ CBaseTFPlayer* pPlayer = (CBaseTFPlayer *)CBaseEntity::Instance( engine->PEntityOfEntIndex( iEntIndex ) );
+ if (!pPlayer)
+ return false;
+ return pPlayer->IsCamouflaged();
+}
+
+static inline bool IsPlayerVisible( int iEntIndex )
+{
+ CBaseTFPlayer* pPlayer = (CBaseTFPlayer *)CBaseEntity::Instance( engine->PEntityOfEntIndex( iEntIndex ) );
+ if (!pPlayer)
+ return false;
+ return pPlayer->PlayerClass() != TFCLASS_UNDECIDED;
+}
+
+static inline bool IsEntityAnObject( int iEntIndex )
+{
+ CBaseEntity* pEnt = CBaseEntity::Instance( engine->PEntityOfEntIndex( iEntIndex ) );
+ CBaseObject *pObject = dynamic_cast<CBaseObject*>(pEnt);
+ if (!pObject)
+ return false;
+
+ // Don't bother with boring ones... they're boring!
+ return ((pObject->GetObjectFlags( ) & OF_SUPPRESS_VISIBLE_TO_TACTICAL) == 0);
+}
+
+#endif
+
+// Visibility defines
+#define PLAYER_VISIBILITY_DISTANCE 2000 // Distance around a player that's exposed on the tactical map
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the entity is visible on this player's tactical map
+//-----------------------------------------------------------------------------
+bool IsEntityVisibleToTactical( int iLocalTeamNumber, int iLocalTeamPlayers,
+ int iLocalTeamObjects, int entIndex, const char *pEntName, int iEntTeamNumber, const Vector &entOrigin )
+{
+ // Resource zones are always visible
+ if ( !strcmp( pEntName, "trigger_resourcezone") )
+ return true;
+
+ // Tunnels are always visible
+ if ( !strcmp( pEntName, "obj_tunnel") || !strcmp( pEntName, "obj_tunnel_prop") )
+ return true;
+
+ // Fixed shields are never visible
+ if ( !strcmp( pEntName, "shield") )
+ return false;
+
+ // NOTE: If you're looking for various object types, fix the ugly hack
+ // in mapdata.cpp!!
+ if ( iLocalTeamNumber == iEntTeamNumber )
+ {
+ // Objects are always visible to their team
+ if (IsEntityAnObject( entIndex ))
+ return true;
+
+ // Players are always visible to their team
+ if (!Q_strncmp( pEntName, "player", 7) )
+ return true;
+
+ // Resource collectors are always visible to their team
+ if ( !strcmp( pEntName, "npc_rescollector_aerial") )
+ return true;
+ }
+
+ return false;
+}
+
+
+
diff --git a/game/shared/tf2/tf_usermessages.cpp b/game/shared/tf2/tf_usermessages.cpp
new file mode 100644
index 0000000..15266d2
--- /dev/null
+++ b/game/shared/tf2/tf_usermessages.cpp
@@ -0,0 +1,43 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "usermessages.h"
+#include "shake.h"
+#include "voice_gamemgr.h"
+
+void RegisterUserMessages( void )
+{
+ usermessages->Register( "Geiger", 1 );
+ usermessages->Register( "Train", 1 );
+ usermessages->Register( "HudText", -1 );
+ usermessages->Register( "SayText", -1 );
+ usermessages->Register( "TextMsg", -1 );
+ usermessages->Register( "HudMsg", -1 );
+ usermessages->Register( "ResetHUD", 1 ); // called every respawn
+ usermessages->Register( "GameTitle", 0 );
+ usermessages->Register( "ItemPickup", -1 );
+ usermessages->Register( "ShowMenu", -1 );
+ usermessages->Register( "Shake", 13 );
+ usermessages->Register( "Fade", 10 );
+ usermessages->Register( "VGUIMenu", -1 ); // Show VGUI menu
+
+ usermessages->Register( "VoiceMask", VOICE_MAX_PLAYERS_DW*4 * 2 + 1 );
+ usermessages->Register( "RequestState", 0 );
+ usermessages->Register( "CloseCaption", -1 ); // Show a caption (by string id number)(duration in 10th of a second)
+ usermessages->Register( "HintText", -1 );
+ usermessages->Register( "AmmoDenied", 2 );
+
+ // TF User messages
+ usermessages->Register( "Damage", 13 );
+ usermessages->Register( "Accuracy", 2 );
+ usermessages->Register( "ZoneState", 1 );
+ usermessages->Register( "Technology", -1 );
+ usermessages->Register( "ActBegin", -1 );
+ usermessages->Register( "ActEnd", -1 );
+ usermessages->Register( "MinimapPulse", -1 );
+ usermessages->Register( "PickupRes", 1 );
+} \ No newline at end of file
diff --git a/game/shared/tf2/tf_vehicleshared.h b/game/shared/tf2/tf_vehicleshared.h
new file mode 100644
index 0000000..db735d5
--- /dev/null
+++ b/game/shared/tf2/tf_vehicleshared.h
@@ -0,0 +1,47 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TF_VEHICLESHARED_H
+#define TF_VEHICLESHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+enum VehicleModeDeploy_e
+{
+ VEHICLE_MODE_NORMAL = 0,
+ VEHICLE_MODE_DEPLOYING,
+ VEHICLE_MODE_UNDEPLOYING,
+ VEHICLE_MODE_DEPLOYED
+};
+#define NUM_VEHICLE_DEPLOYMODE_BITS 2
+
+
+// Attachment indices.
+#define TANK_ATTACHMENT_TURRET_FIREPOS 1
+#define TANK_ATTACHMENT_TURRET_BASE 2
+#define TANK_ATTACHMENT_PLAYER_WAIST 3
+
+// Tread indices.
+enum TreadIndex
+{
+ TREAD_LEFT=0,
+ TREAD_RIGHT=1
+};
+
+// Tread states (send across the wire).
+enum TreadState
+{
+ TREAD_NOTMOVING=0,
+ TREAD_FORWARD=1,
+ TREAD_BACKWARD=2
+};
+
+
+
+#endif // TF_VEHICLESHARED_H
diff --git a/game/shared/tf2/tfclassdata_shared.cpp b/game/shared/tf2/tfclassdata_shared.cpp
new file mode 100644
index 0000000..00273d6
--- /dev/null
+++ b/game/shared/tf2/tfclassdata_shared.cpp
@@ -0,0 +1,58 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tfclassdata_shared.h"
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassCommandoData_t )
+
+ DEFINE_PRED_FIELD( m_bCanBullRush, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bBullRush, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_vecBullRushDir, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_vecBullRushViewDir, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_vecBullRushViewGoalDir, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flBullRushTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flDoubleTapForwardTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassReconData_t )
+
+ DEFINE_FIELD( m_nJumpCount, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flSuppressionJumpTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flSuppressionImpactTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flActiveJumpTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flStickTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_vecImpactNormal, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flImpactDist, FIELD_FLOAT ),
+ DEFINE_FIELD( m_vecUnstickVelocity, FIELD_VECTOR ),
+ DEFINE_FIELD( m_bTrailParticles, FIELD_BOOLEAN ),
+
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassDefenderData_t )
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassEscortData_t )
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassInfiltratorData_t )
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassMedicData_t )
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassSniperData_t )
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassSupportData_t )
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassSapperData_t )
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( PlayerClassPyroData_t )
+END_PREDICTION_DATA()
diff --git a/game/shared/tf2/tfclassdata_shared.h b/game/shared/tf2/tfclassdata_shared.h
new file mode 100644
index 0000000..9a35d04
--- /dev/null
+++ b/game/shared/tf2/tfclassdata_shared.h
@@ -0,0 +1,368 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef TFCLASSDATA_SHARED_H
+#define TFCLASSDATA_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "mathlib/vector.h"
+
+
+enum TFClass
+{
+ TFCLASS_UNDECIDED = 0,
+
+ TFCLASS_RECON,
+ TFCLASS_COMMANDO,
+ TFCLASS_MEDIC,
+ TFCLASS_DEFENDER,
+ TFCLASS_SNIPER,
+ TFCLASS_SUPPORT,
+ TFCLASS_ESCORT,
+ TFCLASS_SAPPER,
+ TFCLASS_INFILTRATOR,
+ TFCLASS_PYRO,
+
+ // TFCLASS_INDIRECT,
+
+ TFCLASS_CLASS_COUNT,
+};
+
+
+//=============================================================================
+//
+// Class Shared Data
+//
+#define PLAYERCLASS_HULL_STAND_MIN Vector( -24.0f, -24.0f, 0.0f )
+#define PLAYERCLASS_HULL_STAND_MAX Vector( 24.0f, 24.0f, 72.0f )
+#define PLAYERCLASS_VIEWOFFSET_STAND Vector( 0.0f, 0.0f, 64.0f )
+
+#define PLAYERCLASS_HULL_DUCK_MIN Vector( -24.0f, -24.0f, 0.0f )
+#define PLAYERCLASS_HULL_DUCK_MAX Vector( 24.0f, 24.0f, 36.0f )
+#define PLAYERCLASS_VIEWOFFSET_DUCK Vector( 0.0f, 0.0f, 30.0f )
+
+#define PLAYERCLASS_STEPSIZE 18.0f
+
+//=============================================================================
+//
+// Commando Class Specific Data
+//
+//#define COMMANDO_TEST
+
+#ifndef COMMANDO_TEST
+
+#define COMMANDOCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define COMMANDOCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define COMMANDOCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define COMMANDOCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define COMMANDOCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define COMMANDOCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define COMMANDOCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+
+#else
+
+#define COMMANDOCLASS_HULL_STAND_MIN Vector( -18.0f, -18.0f, 0.0f )
+#define COMMANDOCLASS_HULL_STAND_MAX Vector( 18.0f, 18.0f, 54.0f )
+#define COMMANDOCLASS_VIEWOFFSET_STAND Vector( 0.0f, 0.0f, 51.0f )
+
+#define COMMANDOCLASS_HULL_DUCK_MIN Vector( -18.0f, -18.0f, 0.0f )
+#define COMMANDOCLASS_HULL_DUCK_MAX Vector( 18.0f, 18.0f, 40.0f )
+#define COMMANDOCLASS_VIEWOFFSET_DUCK Vector( 0.0f, 0.0f, 35.0f )
+
+#define COMMANDOCLASS_STEPSIZE 18.0f
+
+#endif
+
+#define COMMANDO_MOVETYPE_BULLRUSH ( MOVETYPE_LAST + 1 )
+
+#define COMMANDO_TIME_INVALID -9999.0f
+#define COMMANDO_DOUBLETAP_TIME 300.0f
+#define COMMANDO_BULLRUSH_TIME 2000.0f
+#define COMMANDO_BULLRUSH_VIEWDELTA_TIME 1000.0f
+#define COMMANDO_BULLRUSH_VIEWDELTA_TEST ( COMMANDO_BULLRUSH_TIME - COMMANDO_BULLRUSH_VIEWDELTA_TIME )
+
+struct PlayerClassCommandoData_t
+{
+ DECLARE_PREDICTABLE();
+ DECLARE_CLASS_NOBASE( PlayerClassCommandoData_t );
+ DECLARE_EMBEDDED_NETWORKVAR();
+
+ enum { PLAYERCLASS_ID = TFCLASS_COMMANDO };
+
+ CNetworkVar( bool, m_bCanBullRush );
+ CNetworkVar( bool, m_bBullRush );
+ CNetworkVector( m_vecBullRushDir );
+ CNetworkQAngle( m_vecBullRushViewDir );
+ CNetworkQAngle( m_vecBullRushViewGoalDir );
+ CNetworkVar( float, m_flBullRushTime );
+ CNetworkVar( float, m_flDoubleTapForwardTime );
+};
+
+
+//=============================================================================
+//
+// Defender Class Specific Data
+//
+#if 0
+#define DEFENDERCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define DEFENDERCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define DEFENDERCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define DEFENDERCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define DEFENDERCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define DEFENDERCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define DEFENDERCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+#else
+#define DEFENDERCLASS_HULL_STAND_MIN Vector( -18.0f, -18.0f, 0.0f )
+#define DEFENDERCLASS_HULL_STAND_MAX Vector( 18.0f, 18.0f, 55.0f )
+#define DEFENDERCLASS_VIEWOFFSET_STAND Vector( 0.0f, 0.0f, 53.0f )
+
+#define DEFENDERCLASS_HULL_DUCK_MIN Vector( -18.0f, -18.0f, 0.0f )
+#define DEFENDERCLASS_HULL_DUCK_MAX Vector( 18.0f, 18.0f, 30.0f )
+#define DEFENDERCLASS_VIEWOFFSET_DUCK Vector( 0.0f, 0.0f, 25.0f )
+
+#define DEFENDERCLASS_STEPSIZE 15.0f
+#endif
+
+struct PlayerClassDefenderData_t
+{
+ DECLARE_PREDICTABLE();
+
+ enum { PLAYERCLASS_ID = TFCLASS_DEFENDER };
+
+};
+
+//=============================================================================
+//
+// Escort Class Specific Data
+//
+#if 0
+#define ESCORTCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define ESCORTCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define ESCORTCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define ESCORTCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define ESCORTCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define ESCORTCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define ESCORTCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+#else
+#define ESCORTCLASS_HULL_STAND_MIN Vector( -24.0f, -24.0f, 0.0f )
+#define ESCORTCLASS_HULL_STAND_MAX Vector( 24.0f, 24.0f, 74.0f )
+#define ESCORTCLASS_VIEWOFFSET_STAND Vector( 0.0f, 0.0f, 67.0f )
+
+#define ESCORTCLASS_HULL_DUCK_MIN Vector( -24.0f, -24.0f, 0.0f )
+#define ESCORTCLASS_HULL_DUCK_MAX Vector( 24.0f, 24.0f, 72.0f )
+#define ESCORTCLASS_VIEWOFFSET_DUCK Vector( 0.0f, 0.0f, 48.0f )
+
+#define ESCORTCLASS_STEPSIZE 18.0f
+#endif
+
+struct PlayerClassEscortData_t
+{
+ DECLARE_PREDICTABLE();
+
+ enum { PLAYERCLASS_ID = TFCLASS_ESCORT };
+
+};
+
+//=============================================================================
+//
+// Infiltrator Class Specific Data
+//
+#define INFILTRATORCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define INFILTRATORCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define INFILTRATORCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define INFILTRATORCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define INFILTRATORCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define INFILTRATORCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define INFILTRATORCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+
+struct PlayerClassInfiltratorData_t
+{
+ DECLARE_PREDICTABLE();
+
+ enum { PLAYERCLASS_ID = TFCLASS_INFILTRATOR };
+
+};
+
+
+//=============================================================================
+//
+// Pyro Class Specific Data
+//
+
+#define PYROCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define PYROCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define PYROCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define PYROCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define PYROCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define PYROCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define PYROCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+
+struct PlayerClassPyroData_t
+{
+ DECLARE_PREDICTABLE();
+
+ enum { PLAYERCLASS_ID = TFCLASS_PYRO };
+
+};
+
+
+//=============================================================================
+//
+// Medic Class Specific Data
+//
+#define MEDICCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define MEDICCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define MEDICCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define MEDICCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define MEDICCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define MEDICCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define MEDICCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+
+struct PlayerClassMedicData_t
+{
+ DECLARE_PREDICTABLE();
+
+ enum { PLAYERCLASS_ID = TFCLASS_MEDIC };
+
+};
+
+//=============================================================================
+//
+// Recon Class Specific Data
+//
+#define RECONCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define RECONCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define RECONCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define RECONCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define RECONCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define RECONCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define RECONCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+
+struct PlayerClassReconData_t
+{
+ DECLARE_PREDICTABLE();
+ DECLARE_CLASS_NOBASE( PlayerClassReconData_t );
+ DECLARE_EMBEDDED_NETWORKVAR();
+
+
+ enum { PLAYERCLASS_ID = TFCLASS_RECON };
+
+ // For in-air jumps
+ CNetworkVar( int, m_nJumpCount );
+
+ // For wall jumps
+ CNetworkVar( float, m_flSuppressionJumpTime );
+ CNetworkVar( float, m_flSuppressionImpactTime );
+ CNetworkVar( float, m_flActiveJumpTime );
+ CNetworkVar( float, m_flStickTime );
+ CNetworkVector( m_vecImpactNormal );
+ CNetworkVar( float, m_flImpactDist );
+ CNetworkVector( m_vecUnstickVelocity );
+
+ // Trail
+ CNetworkVar( bool, m_bTrailParticles );
+};
+
+
+//=============================================================================
+//
+// Sniper Class Specific Data
+//
+#define SNIPERCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define SNIPERCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define SNIPERCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define SNIPERCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define SNIPERCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define SNIPERCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define SNIPERCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+
+struct PlayerClassSniperData_t
+{
+ DECLARE_PREDICTABLE();
+
+ enum { PLAYERCLASS_ID = TFCLASS_SNIPER };
+
+};
+
+//=============================================================================
+//
+// Support Class Specific Data
+//
+#if 0
+#define SUPPORTCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define SUPPORTCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define SUPPORTCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define SUPPORTCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define SUPPORTCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define SUPPORTCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define SUPPORTCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+#else
+#define SUPPORTCLASS_HULL_STAND_MIN Vector( -30.0f, -30.0f, 0.0f )
+#define SUPPORTCLASS_HULL_STAND_MAX Vector( 30.0f, 30.0f, 106.0f )
+#define SUPPORTCLASS_VIEWOFFSET_STAND Vector( 0.0f, 0.0f, 120.0f )
+
+#define SUPPORTCLASS_HULL_DUCK_MIN Vector( -30.0f, -30.0f, 0.0f )
+#define SUPPORTCLASS_HULL_DUCK_MAX Vector( 30.0f, 30.0f, 72.0f )
+#define SUPPORTCLASS_VIEWOFFSET_DUCK Vector( 0.0f, 0.0f, 64.0f )
+
+#define SUPPORTCLASS_STEPSIZE 27.0f
+#endif
+
+struct PlayerClassSupportData_t
+{
+ DECLARE_PREDICTABLE();
+
+ enum { PLAYERCLASS_ID = TFCLASS_SUPPORT };
+
+};
+
+//=============================================================================
+//
+// Sapper Class Specific Data
+//
+#define SAPPERCLASS_HULL_STAND_MIN PLAYERCLASS_HULL_STAND_MIN
+#define SAPPERCLASS_HULL_STAND_MAX PLAYERCLASS_HULL_STAND_MAX
+#define SAPPERCLASS_VIEWOFFSET_STAND PLAYERCLASS_VIEWOFFSET_STAND
+
+#define SAPPERCLASS_HULL_DUCK_MIN PLAYERCLASS_HULL_DUCK_MIN
+#define SAPPERCLASS_HULL_DUCK_MAX PLAYERCLASS_HULL_DUCK_MAX
+#define SAPPERCLASS_VIEWOFFSET_DUCK PLAYERCLASS_VIEWOFFSET_DUCK
+
+#define SAPPERCLASS_STEPSIZE PLAYERCLASS_STEPSIZE
+
+struct PlayerClassSapperData_t
+{
+ DECLARE_PREDICTABLE();
+
+ enum { PLAYERCLASS_ID = TFCLASS_SAPPER };
+};
+
+
+#include "tf_shareddefs.h"
+
+
+#endif // TFCLASSDATA_SHARED_H
diff --git a/game/shared/tf2/vehicle_mortar_shared.h b/game/shared/tf2/vehicle_mortar_shared.h
new file mode 100644
index 0000000..adbe60b
--- /dev/null
+++ b/game/shared/tf2/vehicle_mortar_shared.h
@@ -0,0 +1,18 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef VEHICLE_MORTAR_SHARED_H
+#define VEHICLE_MORTAR_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// How long it takes to deploy the mortar.
+#define VEHICLE_MORTAR_DEPLOY_WAIT_TIME 3
+
+
+#endif // VEHICLE_MORTAR_SHARED_H
diff --git a/game/shared/tf2/weapon_arcwelder.cpp b/game/shared/tf2/weapon_arcwelder.cpp
new file mode 100644
index 0000000..27c6ae3
--- /dev/null
+++ b/game/shared/tf2/weapon_arcwelder.cpp
@@ -0,0 +1,322 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "in_buttons.h"
+#include "tf_gamerules.h"
+#include "weapon_combatshield.h"
+
+#if defined( CLIENT_DLL )
+
+#include "particles_simple.h"
+#include "fx.h"
+#include "fx_quad.h"
+#include "clienteffectprecachesystem.h"
+
+#define CWeaponArcWelder C_WeaponArcWelder
+#else
+#endif
+
+#include "weapon_repairgun.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+
+// Buff ranges
+ConVar weapon_arcwelder_target_range( "weapon_arcwelder_target_range", "90", FCVAR_REPLICATED, "The farthest away you can be for the arcwelder to initially lock onto a target." );
+ConVar weapon_arcwelder_stick_range( "weapon_arcwelder_stick_range", "100", FCVAR_REPLICATED, "How far away the arcwelder can stay locked onto someone." );
+ConVar weapon_arcwelder_rate( "weapon_arcwelder_rate", "15", FCVAR_REPLICATED );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponArcWelder : public CWeaponRepairGun
+{
+ DECLARE_CLASS( CWeaponArcWelder, CWeaponRepairGun );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponArcWelder( void );
+
+ virtual void Precache();
+
+ virtual float GetTargetRange( void );
+ virtual float GetStickRange( void );
+ virtual float GetHealRate( void );
+ virtual bool AppliesModifier( void ) { return false; }
+ virtual bool TargetsPlayers( void ) { return false; }
+ virtual CBaseEntity *GetTargetToHeal( CBaseEntity *pCurHealing );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual void ClientThink( void );
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+ virtual void ViewModelDrawn( C_BaseViewModel *pViewModel );
+
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+
+private:
+ bool m_bWelding;
+
+#if defined( CLIENT_DLL )
+ float m_flNextEffectTime;
+#endif
+
+private:
+ CWeaponArcWelder( const CWeaponArcWelder & );
+};
+
+LINK_ENTITY_TO_CLASS( weapon_arcwelder, CWeaponArcWelder );
+
+PRECACHE_WEAPON_REGISTER( weapon_arcwelder );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponArcWelder, DT_WeaponArcWelder )
+
+BEGIN_NETWORK_TABLE( CWeaponArcWelder, DT_WeaponArcWelder )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponArcWelder )
+END_PREDICTION_DATA()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponArcWelder::CWeaponArcWelder()
+{
+ SetPredictionEligible( true );
+ m_bWelding = false;
+#ifdef CLIENT_DLL
+ m_flNextEffectTime = 0;
+#endif
+}
+
+void CWeaponArcWelder::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "WeaponRepairGun.Healing" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponArcWelder::GetTargetRange( void )
+{
+ return weapon_arcwelder_target_range.GetFloat();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponArcWelder::GetStickRange( void )
+{
+ return weapon_arcwelder_target_range.GetFloat();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponArcWelder::GetHealRate( void )
+{
+ return weapon_arcwelder_rate.GetFloat();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a pointer to a healable target
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponArcWelder::GetTargetToHeal( CBaseEntity *pCurHealing )
+{
+ CBaseEntity *pTarget = BaseClass::GetTargetToHeal(pCurHealing);
+ if ( !pTarget )
+ return pTarget;
+
+ // Make sure the target is within our field of view
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return NULL;
+
+ Vector vecAiming;
+ pOwner->EyeVectors( &vecAiming );
+
+ // Find a player in range of this player, and make sure they're healable.
+ Vector vecSrc = pOwner->Weapon_ShootPosition( );
+ Vector vecEnd = vecSrc + vecAiming * GetTargetRange();
+ trace_t tr;
+
+ // Use WeaponTraceLine so shields are tested...
+ TFGameRules()->WeaponTraceLine( vecSrc, vecEnd, (MASK_SHOT & ~CONTENTS_HITBOX), pOwner, DMG_PROBE, &tr );
+ if ( tr.fraction != 1.0 && tr.m_pEnt == pTarget )
+ return pTarget;
+
+ return NULL;
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponArcWelder::ClientThink( void )
+{
+ CBasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( !pPlayer )
+ return;
+
+ if ( m_hHealingTarget == NULL )
+ return;
+
+ // Don't show it while the player is dead. Ideally, we'd respond to m_bHealing in OnDataChanged,
+ // but it stops sending the weapon when it's holstered, and it gets holstered when the player dies.
+ C_BasePlayer *pFiringPlayer = dynamic_cast< C_BasePlayer* >( GetOwner() );
+ if ( !pFiringPlayer || pFiringPlayer->IsPlayerDead() )
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_NEVER );
+ m_bPlayingSound = false;
+ StopRepairSound();
+ return;
+ }
+
+ // Start playing the heal sound, if we're not already
+ if ( !m_bPlayingSound )
+ {
+ m_bPlayingSound = true;
+ CLocalPlayerFilter filter;
+ EmitSound( filter, entindex(), "WeaponRepairGun.Healing" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponArcWelder::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return true;
+
+ switch ( event )
+ {
+ case 7001:
+ m_bWelding = true;
+ return true;
+ case 7002:
+ m_bWelding = false;
+ return true;
+ default:
+ break;
+ };
+
+ return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
+}
+
+CLIENTEFFECT_REGISTER_BEGIN( PrecacheArcWelderEffect )
+CLIENTEFFECT_MATERIAL( "particle/smoke_arcwelder" )
+CLIENTEFFECT_MATERIAL( "effects/spark2" )
+CLIENTEFFECT_MATERIAL( "effects/blueflare" )
+CLIENTEFFECT_MATERIAL( "effects/blueflare2" )
+CLIENTEFFECT_REGISTER_END()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponArcWelder::ViewModelDrawn( C_BaseViewModel *pViewModel )
+{
+ if ( !m_bWelding || !m_hHealingTarget.Get() )
+ return;
+
+ if ( m_flNextEffectTime > gpGlobals->curtime )
+ return;
+ m_flNextEffectTime = gpGlobals->curtime + 0.1;
+
+ // Get our weldpoint
+ Vector attachOrigin;
+ QAngle attachAngles;
+ pViewModel->GetAttachment( pViewModel->LookupAttachment("muzzle"), attachOrigin, attachAngles );
+ Vector vecEnd = m_hHealingTarget->WorldSpaceCenter();
+ trace_t tr;
+
+ // Use WeaponTraceLine so shields are tested...
+ TFGameRules()->WeaponTraceLine( attachOrigin, vecEnd, (MASK_SHOT & ~CONTENTS_HITBOX), GetOwner(), DMG_PROBE, &tr );
+
+ // Smoke
+ unsigned char color[3];
+ int iColOff = random->RandomInt(-16,16);
+ color[0] = 120 + iColOff;
+ color[1] = 230 + iColOff;
+ color[2] = 235 + iColOff;
+ /*
+ // Pull out from the target a bit
+ Vector vecOrigin = vecEnd;
+ Vector vecFromTarget = (vecEnd - attachOrigin);
+ VectorNormalize( vecFromTarget );
+ vecOrigin -= (vecFromTarget * 24);
+ */
+
+ Vector vecOrigin = attachOrigin;
+ // Velocity
+ Vector vecVelocity = tr.plane.normal;
+ vecVelocity.z += random->RandomFloat( 8, 12 );
+ // Add it
+ CSmartPtr<CSimpleEmitter> pSimple = FX_Smoke( vecOrigin, vecVelocity,
+ random->RandomFloat( 4, 8 ), // Scale
+ 1,
+ random->RandomFloat( 0.5, 3.0 ), // Dietime
+ color,
+ random->RandomInt( 64, 200 ), // Alpha
+ "particle/smoke_arcwelder",
+ random->RandomInt(0,255), // Roll
+ 0 ); // Rolldelta
+
+ // Sparks
+ FX_Sparks( vecOrigin, 1, 4, tr.plane.normal, 2.5, 8, 64, "effects/spark2" );
+
+ // Bright Glow
+ SimpleParticle *sParticle;
+ sParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/blueflare" ), vecOrigin );
+ if ( sParticle == NULL )
+ return;
+ sParticle->m_flLifetime = 0.0f;
+ sParticle->m_flDieTime = 0.5f;
+ sParticle->m_vecVelocity.Init();
+ sParticle->m_uchColor[0] = 255;
+ sParticle->m_uchColor[1] = 255;
+ sParticle->m_uchColor[2] = 255;
+ sParticle->m_uchStartSize = random->RandomInt(5,7);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize;
+ sParticle->m_flRoll = random->RandomInt(0,360);
+ sParticle->m_flRollDelta = 0;
+
+ // Dull Glow
+ sParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), pSimple->GetPMaterial( "effects/blueflare2" ), vecOrigin );
+ if ( sParticle == NULL )
+ return;
+ sParticle->m_flLifetime = 0.0f;
+ sParticle->m_flDieTime = 0.2f;
+ sParticle->m_vecVelocity.Init();
+ sParticle->m_uchColor[0] = 255;
+ sParticle->m_uchColor[1] = 255;
+ sParticle->m_uchColor[2] = 255;
+ sParticle->m_uchStartSize = random->RandomInt(15,20);
+ sParticle->m_uchEndSize = sParticle->m_uchStartSize;
+ sParticle->m_flRoll = random->RandomInt(0,360);
+ sParticle->m_flRollDelta = 0;
+}
+#endif
diff --git a/game/shared/tf2/weapon_basecombatobject.cpp b/game/shared/tf2/weapon_basecombatobject.cpp
new file mode 100644
index 0000000..4d02d43
--- /dev/null
+++ b/game/shared/tf2/weapon_basecombatobject.cpp
@@ -0,0 +1,126 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_basecombatobject.h"
+//====================================================================================================
+// BASE COMBAT OBJECT WEAPON
+//====================================================================================================
+
+LINK_ENTITY_TO_CLASS( weapon_basecombatobject, CWeaponBaseCombatObject );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponBaseCombatObject, DT_WeaponBaseCombatObject )
+
+BEGIN_NETWORK_TABLE( CWeaponBaseCombatObject, DT_WeaponBaseCombatObject )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponBaseCombatObject )
+
+/*
+ DEFINE_PRED_ARRAY( m_szObjectName, FIELD_CHARACTER, sizeof( m_szObjectName ) ),
+ DEFINE_PRED_FIELD( m_vecBuildMins, FIELD_VECTOR, 0 ),
+ DEFINE_PRED_FIELD( m_vecBuildMaxs, FIELD_VECTOR, 0 ),
+*/
+
+END_PREDICTION_DATA()
+
+
+CWeaponBaseCombatObject::CWeaponBaseCombatObject()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Place the combat object
+//-----------------------------------------------------------------------------
+void CWeaponBaseCombatObject::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = dynamic_cast<CBaseTFPlayer*>((CBaseEntity*)GetOwner());
+ if ( !pPlayer )
+ return;
+ if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ return;
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+
+ Vector vecPlaceOrigin;
+ QAngle angPlaceAngles;
+ if ( GetPlacePosition( pPlayer, &vecPlaceOrigin, &angPlaceAngles ) == false )
+ {
+ WeaponSound( WPN_DOUBLE );
+ return;
+ }
+
+ // Place the combat object
+ PlaceCombatObject( pPlayer, vecPlaceOrigin, angPlaceAngles );
+
+ WeaponSound( SINGLE );
+ pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
+
+ // If I'm now out of ammo, switch away
+ if ( !HasPrimaryAmmo() )
+ {
+ pPlayer->SelectLastItem();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if we found a valid placement point
+//-----------------------------------------------------------------------------
+bool CWeaponBaseCombatObject::GetPlacePosition( CBaseTFPlayer *pBuilder, Vector *vecPlaceOrigin, QAngle *angPlaceAngles )
+{
+ Vector vecForward;
+ QAngle vecAngles = vec3_angle;
+ vecAngles.y = pBuilder->EyeAngles().y;
+ AngleVectors( vecAngles, &vecForward, NULL, NULL);
+ *vecPlaceOrigin = pBuilder->WorldSpaceCenter() + (vecForward * ((m_vecBuildMaxs.x - m_vecBuildMins.x) + 32));
+ if ( UTIL_PointContents( *vecPlaceOrigin ) != CONTENTS_EMPTY )
+ return false;
+
+ // Room to fit?
+ trace_t tr;
+ UTIL_TraceHull( *vecPlaceOrigin, *vecPlaceOrigin + Vector(0,0,-64), m_vecBuildMins, m_vecBuildMaxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+ if ( tr.allsolid || tr.startsolid )
+ return false;
+ if ( tr.fraction == 1.0 )
+ return false;
+
+ *vecPlaceOrigin = tr.endpos;
+ //VectorAngles( tr.plane.normal, *angPlaceAngles );
+ *angPlaceAngles = QAngle(0,0,0);
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Put the combat object for this weapon on the specified point
+//-----------------------------------------------------------------------------
+void CWeaponBaseCombatObject::PlaceCombatObject( CBaseTFPlayer *pBuilder, Vector vecOrigin, QAngle angles )
+{
+#if !defined( CLIENT_DLL )
+ Assert( m_szObjectName != NULL );
+
+ CBaseEntity *pEntity = CreateEntityByName( m_szObjectName );
+ pEntity->SetLocalAngles( angles );
+ pEntity->Spawn();
+ pEntity->Teleport( &vecOrigin, &angles, &vec3_origin );
+
+ // If it's an object, set it's builder & team
+ CBaseObject *pObject = dynamic_cast< CBaseObject * >( pEntity );
+ if ( pObject )
+ {
+ pObject->SetBuilder( pBuilder );
+ pObject->ChangeTeam( pBuilder->GetTeamNumber() );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponBaseCombatObject::GetFireRate( void )
+{
+ return 0.5;
+}
diff --git a/game/shared/tf2/weapon_basecombatobject.h b/game/shared/tf2/weapon_basecombatobject.h
new file mode 100644
index 0000000..37e802f
--- /dev/null
+++ b/game/shared/tf2/weapon_basecombatobject.h
@@ -0,0 +1,68 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_BASECOMBATOBJECT_H
+#define WEAPON_BASECOMBATOBJECT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetfcombatweapon_shared.h"
+
+class CBaseTFPlayer;
+
+#if defined( CLIENT_DLL )
+
+#define CWeaponBaseCombatObject C_WeaponBaseCombatObject
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Base class for combat object weapons
+//-----------------------------------------------------------------------------
+class CWeaponBaseCombatObject : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponBaseCombatObject, CBaseTFCombatWeapon );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponBaseCombatObject();
+
+ virtual void PrimaryAttack( void );
+ virtual bool GetPlacePosition( CBaseTFPlayer *pBuilder, Vector *vecPlaceOrigin, QAngle *angPlaceAngles );
+ virtual void PlaceCombatObject( CBaseTFPlayer *pBuilder, Vector vecOrigin, QAngle angles );
+ virtual float GetFireRate( void );
+
+protected:
+ char *m_szObjectName;
+ Vector m_vecBuildMins;
+ Vector m_vecBuildMaxs;
+
+ /*
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+ */
+
+private:
+ CWeaponBaseCombatObject( const CWeaponBaseCombatObject & );
+};
+
+#endif // WEAPON_BASECOMBATOBJECT_H
diff --git a/game/shared/tf2/weapon_builder.cpp b/game/shared/tf2/weapon_builder.cpp
new file mode 100644
index 0000000..7d90410
--- /dev/null
+++ b/game/shared/tf2/weapon_builder.cpp
@@ -0,0 +1,530 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The "weapon" used to build objects
+//
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "tf_basecombatweapon.h"
+#include "EntityList.h"
+#include "in_buttons.h"
+#include "weapon_builder.h"
+#include "tf_obj.h"
+#include "sendproxy.h"
+#include "weapon_objectselection.h"
+#include "info_act.h"
+#include "vguiscreen.h"
+
+extern ConVar tf2_object_hard_limits;
+extern ConVar tf_fastbuild;
+
+EXTERN_SEND_TABLE(DT_BaseCombatWeapon)
+
+IMPLEMENT_SERVERCLASS_ST(CWeaponBuilder, DT_WeaponBuilder)
+ SendPropInt( SENDINFO( m_iBuildState ), 4, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iCurrentObject ), BUILDER_OBJECT_BITS, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iCurrentObjectState ), 4, SPROP_UNSIGNED ),
+ SendPropEHandle( SENDINFO( m_hObjectBeingBuilt ) ),
+ SendPropTime( SENDINFO( m_flStartTime ) ),
+ SendPropTime( SENDINFO( m_flTotalTime ) ),
+ SendPropArray
+ (
+ SendPropInt( SENDINFO_ARRAY(m_bObjectValidity), 1, SPROP_UNSIGNED), m_bObjectValidity
+ ),
+ SendPropArray
+ (
+ SendPropInt( SENDINFO_ARRAY(m_bObjectBuildability), 1, SPROP_UNSIGNED), m_bObjectBuildability
+ ),
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_builder, CWeaponBuilder );
+PRECACHE_WEAPON_REGISTER(weapon_builder);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponBuilder::CWeaponBuilder()
+{
+ for ( int i=0; i < m_bObjectValidity.Count(); i++ )
+ m_bObjectValidity.Set( i, 0 );
+
+ m_iCurrentObject = BUILDER_INVALID_OBJECT;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheModel( "models/weapons/v_slam.mdl" );
+ PrecacheVGuiScreen( "screen_human_pda" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets info about the control panels
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
+{
+ pPanelName = "screen_human_pda";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::ShouldShowControlPanels( void )
+{
+ if ( GetActivity() == ACT_VM_IDLE )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::UpdateOnRemove( void )
+{
+ // Tell the player he's lost his build weapon
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( pOwner && pOwner->GetWeaponBuilder() == this )
+ {
+ pOwner->SetWeaponBuilder( NULL );
+ }
+
+ // Chain at end to mimic destructor unwind order
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Builder weapon has just been given to a player
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::Equip( CBaseCombatCharacter *pOwner )
+{
+ BaseClass::Equip( pOwner );
+ ((CBaseTFPlayer*)pOwner)->SetWeaponBuilder( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a new object type to this build weapon. This will allow
+// the player carrying this builder to build the object.
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::AddBuildableObject( int iObjectType )
+{
+ m_bObjectValidity.Set( iObjectType, true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::CanDeploy( void )
+{
+ if ( m_iCurrentObject != BUILDER_INVALID_OBJECT )
+ {
+ SetCurrentState( BS_PLACING );
+ StartPlacement();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.35f;
+ }
+ else
+ {
+ m_hObjectBeingBuilt = NULL;
+ SetCurrentState( BS_IDLE );
+ SetCurrentObject( m_iCurrentObject );
+ }
+ return BaseClass::CanDeploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::Deploy( )
+{
+#if !defined( CLIENT_DLL )
+ if ( m_hObjectBeingBuilt.Get() && m_hObjectBeingBuilt->IsAnUpgrade() )
+ return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_SLAM_STICKWALL_ND_DRAW, (char*)GetAnimPrefix() );
+
+ return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() );
+#else
+ return true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Prevent switching when working on something
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::CanHolster( void )
+{
+ if ( IsBuilding() )
+ return false;
+
+ return BaseClass::CanHolster();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseCombatWeapon *CWeaponBuilder::GetLastWeapon( void )
+{
+ return BaseClass::GetLastWeapon();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop placement when holstering
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ if ( m_iBuildState == BS_PLACING || m_iBuildState == BS_PLACING_INVALID )
+ {
+ SetCurrentState( BS_IDLE );
+ }
+ StopPlacement();
+
+ return BaseClass::Holster(pSwitchingTo);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Ignore input while the player's building anything
+ if ( pOwner->IsBuilding() )
+ return;
+
+ // Switch away if I'm not in placement mode
+ if ( m_iBuildState != BS_PLACING && m_iBuildState != BS_PLACING_INVALID )
+ {
+ pOwner->SwitchToNextBestWeapon( NULL );
+ return;
+ }
+
+ if (( pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ PrimaryAttack();
+ }
+
+ // Allow shield post frame
+ AllowShieldPostFrame( true );
+
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start placing or building the currently selected object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // What state should we move to?
+ switch( m_iBuildState )
+ {
+ case BS_IDLE:
+ {
+ // Idle state starts selection
+ SetCurrentState( BS_SELECTING );
+ }
+ break;
+
+ case BS_SELECTING:
+ {
+ // Do nothing, client handles selection
+ return;
+ }
+ break;
+
+ case BS_PLACING:
+ {
+ if ( m_hObjectBeingBuilt )
+ {
+ // Give the object a chance to veto the "start building" command. Objects like barbed wire
+ // may want to change their properties instead of actually building yet.
+ if ( m_hObjectBeingBuilt->PreStartBuilding() )
+ {
+ int iFlags = m_hObjectBeingBuilt->GetObjectFlags();
+
+ // Can't build if the game hasn't started
+ if ( !tf_fastbuild.GetInt() && CurrentActIsAWaitingAct() )
+ {
+ ClientPrint( pOwner, HUD_PRINTCENTER, "Can't build until the game's started.\n" );
+ return;
+ }
+
+ StartBuilding();
+
+ // Should we switch away?
+ if ( iFlags & OF_ALLOW_REPEAT_PLACEMENT )
+ {
+ // Start placing another
+ SetCurrentState( BS_PLACING );
+ StartPlacement();
+ }
+ else
+ {
+ pOwner->SwitchToNextBestWeapon( NULL );
+ }
+ }
+ }
+ }
+ break;
+
+ case BS_PLACING_INVALID:
+ {
+ WeaponSound( SINGLE_NPC );
+
+ // If there is any associated error text when placing the object, display it
+ if( m_hObjectBeingBuilt != NULL )
+ {
+ if (m_hObjectBeingBuilt->MustBeBuiltInResourceZone())
+ {
+ ClientPrint( pOwner, HUD_PRINTCENTER, "Only placeable in an empty resource zone.\n" );
+ }
+ else if (m_hObjectBeingBuilt->MustBeBuiltInConstructionYard())
+ {
+ ClientPrint( pOwner, HUD_PRINTCENTER, "Only placeable in a construction yard.\n" );
+ }
+ }
+ }
+ break;
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the builder to the specified state
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::SetCurrentState( int iState )
+{
+ // Check the current build state... we may need to shut some stuff down...
+ switch(m_iBuildState)
+ {
+ case BS_PLACING:
+ case BS_PLACING_INVALID:
+ {
+ if ((iState != BS_PLACING) && (iState != BS_PLACING_INVALID) && (iState != BS_BUILDING))
+ {
+ StopPlacement();
+ WeaponSound( SPECIAL1 );
+ }
+ }
+ break;
+ }
+
+ m_iBuildState = iState;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the builder to the specified object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::SetCurrentObject( int iObject )
+{
+ // Fixup for invalid objects
+ if (iObject < 0)
+ iObject = BUILDER_INVALID_OBJECT;
+
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ int i;
+
+ // If -1 was passed in, set to our first available object
+ if ( iObject == BUILDER_INVALID_OBJECT )
+ {
+ for ( i = 0; i < OBJ_LAST; i++ )
+ {
+ if ( m_bObjectValidity[i] )
+ {
+ iObject = i;
+ break;
+ }
+ }
+ }
+
+ // Recalculate the buildability of each object (for propagation to the client)
+ for ( i=0; i < m_bObjectBuildability.Count(); i++ )
+ m_bObjectBuildability.Set( i, 0 );
+
+ for ( i = 0; i < OBJ_LAST; i++ )
+ {
+ if ( m_bObjectValidity[i] && pOwner->CanBuild(i) == CB_CAN_BUILD )
+ {
+ m_bObjectBuildability.Set( i, true );
+ }
+ }
+
+ m_iCurrentObject = iObject;
+ m_iCurrentObjectState = pOwner->CanBuild( m_iCurrentObject );
+ m_flStartTime = 0;
+ m_flTotalTime = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Idle updates the position of the build placement model
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::WeaponIdle( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // If we're in placement mode, update the placement model
+ switch( m_iBuildState )
+ {
+ case BS_PLACING:
+ case BS_PLACING_INVALID:
+ {
+ if ( UpdatePlacement() )
+ {
+ SetCurrentState( BS_PLACING );
+ }
+ else
+ {
+ SetCurrentState( BS_PLACING_INVALID );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The player holding this weapon has just gained new technology.
+// Check to see if it affects the medikit
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // Force a recalculation of the state for this object
+ SetCurrentObject( m_iCurrentObject );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start placing the object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::StartPlacement( void )
+{
+ StopPlacement();
+
+ // Create the slab
+ m_hObjectBeingBuilt = (CBaseObject*)CreateEntityByName( GetObjectInfo( m_iCurrentObject )->m_pClassName );
+ if ( m_hObjectBeingBuilt )
+ {
+ m_hObjectBeingBuilt->Spawn();
+ m_hObjectBeingBuilt->StartPlacement( ToBaseTFPlayer( GetOwner() ) );
+ UpdatePlacement();
+
+ // Stomp this here in the same frame we make the object, so prevent clientside warnings that it's under attack
+ m_hObjectBeingBuilt->m_iHealth = OBJECT_CONSTRUCTION_STARTINGHEALTH;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the viewmodel according to the type of object we're placing
+//-----------------------------------------------------------------------------
+const char *CWeaponBuilder::GetViewModel( int viewmodelindex /*=0*/ ) const
+{
+ if ( m_hObjectBeingBuilt.Get() && m_hObjectBeingBuilt->IsAnUpgrade() )
+ return "models/weapons/v_slam.mdl";
+
+ return BaseClass::GetViewModel( viewmodelindex );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::StopPlacement( void )
+{
+ if ( m_hObjectBeingBuilt )
+ {
+ m_hObjectBeingBuilt->StopPlacement();
+ m_hObjectBeingBuilt = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Move the placement model to the current position. Return false if it's an invalid position
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::UpdatePlacement( void )
+{
+ if ( !m_hObjectBeingBuilt )
+ return false;
+
+ return m_hObjectBeingBuilt->UpdatePlacement( ToBaseTFPlayer(GetOwner()) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player holding this weapon has started building something
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::StartBuilding( void )
+{
+ if ( m_hObjectBeingBuilt.Get() && UpdatePlacement() )
+ {
+ SetCurrentState( BS_BUILDING );
+ m_hObjectBeingBuilt->StartBuilding( GetOwner() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player holding this weapon has aborted the build of an object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::StoppedBuilding( int iObjectType )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // Force a recalculation of the state for this object
+ SetCurrentObject( m_iCurrentObject );
+ SetCurrentState( BS_IDLE );
+
+ WeaponSound( SPECIAL2 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponBuilder::IsBuilding( void )
+{
+ return ( m_iBuildState == BS_BUILDING );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The player holding this weapon has just finished building an object
+//-----------------------------------------------------------------------------
+void CWeaponBuilder::FinishedObject( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // We're no longer building anything...
+ m_hObjectBeingBuilt = NULL;
+
+ // Force a recalculation of the state for this object
+ SetCurrentObject( m_iCurrentObject );
+ SetCurrentState( BS_IDLE );
+ }
+}
diff --git a/game/shared/tf2/weapon_builder.h b/game/shared/tf2/weapon_builder.h
new file mode 100644
index 0000000..a684dbb
--- /dev/null
+++ b/game/shared/tf2/weapon_builder.h
@@ -0,0 +1,90 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_BUILDER_H
+#define WEAPON_BUILDER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "weapon_combat_usedwithshieldbase.h"
+
+class CBaseObject;
+
+//=========================================================
+// Builder Weapon
+//=========================================================
+class CWeaponBuilder : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponBuilder, CWeaponCombatUsedWithShieldBase );
+public:
+ CWeaponBuilder();
+
+ virtual void UpdateOnRemove( void );
+
+ DECLARE_SERVERCLASS();
+
+ virtual void Precache( void );
+ virtual bool CanDeploy( void );
+ virtual bool CanHolster( void );
+ virtual CBaseCombatWeapon *GetLastWeapon( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual void WeaponIdle( void );
+ virtual bool Deploy( void );
+ virtual const char *GetViewModel( int viewmodelindex = 0 ) const;
+
+ void SetCurrentState( int iState );
+ void SetCurrentObject( int iObject );
+
+ virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
+ virtual void Equip( CBaseCombatCharacter *pOwner );
+
+ // Add a new object type to the list of objects this builder weapon can build
+ void AddBuildableObject( int iObjectType );
+
+ // Placement
+ void StartPlacement( void );
+ void StopPlacement( void );
+ bool UpdatePlacement( void );
+
+ // Building
+ void StartBuilding( void );
+ void StoppedBuilding( int iObjectType );
+ bool IsBuilding( void );
+ void FinishedObject( void );
+
+ virtual void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName );
+
+ virtual bool ShouldShowControlPanels( void );
+
+private:
+ void PerformModifications( CBaseObject* pObject );
+
+public:
+ CNetworkVar( int, m_iBuildState );
+ CNetworkVar( unsigned int, m_iCurrentObject );
+ int m_iCurrentObjectID;
+ CNetworkVar( int, m_iCurrentObjectState );
+
+ // Objects that this builder can build
+ CNetworkArray( bool, m_bObjectValidity, OBJ_LAST );
+ // Buildability of each object
+ CNetworkArray( bool, m_bObjectBuildability, OBJ_LAST );
+
+ // Build data for the current object, propagated when the player starts to build it
+ CNetworkVar( float, m_flStartTime );
+ CNetworkVar( float, m_flTotalTime );
+
+ float m_flLastRepairTime;
+
+ CNetworkHandle( CBaseObject, m_hObjectBeingBuilt );
+};
+
+
+#endif // WEAPON_BUILDER_H
diff --git a/game/shared/tf2/weapon_combat_basegrenade.cpp b/game/shared/tf2/weapon_combat_basegrenade.cpp
new file mode 100644
index 0000000..470c4b6
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_basegrenade.cpp
@@ -0,0 +1,152 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Base class for hand-thrown grenades that work with the handheld shield
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combat_basegrenade.h"
+#include "weapon_combatshield.h"
+#include "in_buttons.h"
+
+LINK_ENTITY_TO_CLASS( weapon_combat_basegrenade, CWeaponCombatBaseGrenade );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatBaseGrenade, DT_WeaponCombatBaseGrenade )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatBaseGrenade, DT_WeaponCombatBaseGrenade )
+#if !defined( CLIENT_DLL )
+ SendPropTime( SENDINFO( m_flStartedThrowAt ) ),
+#else
+ RecvPropTime( RECVINFO( m_flStartedThrowAt ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatBaseGrenade )
+
+ DEFINE_PRED_FIELD_TOL( m_flStartedThrowAt, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+
+END_PREDICTION_DATA()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponCombatBaseGrenade::CWeaponCombatBaseGrenade( void )
+{
+ m_flStartedThrowAt = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponCombatBaseGrenade::GetFireRate( void )
+{
+ return 2.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatBaseGrenade::ItemPostFrame( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ AllowShieldPostFrame( !m_flStartedThrowAt );
+
+ // Look for button downs
+ if ( (pOwner->m_nButtons & IN_ATTACK) && GetShieldState() == SS_DOWN && !m_flStartedThrowAt && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ m_flStartedThrowAt = gpGlobals->curtime;
+
+ SendWeaponAnim( ACT_VM_DRAW );
+ }
+
+ // Look for button ups
+ if ( (pOwner->m_afButtonReleased & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) && m_flStartedThrowAt )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime;
+ PrimaryAttack();
+ m_flStartedThrowAt = 0;
+ }
+
+ // No buttons down?
+ if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ WeaponIdle( );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatBaseGrenade::PrimaryAttack( void )
+{
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( !ComputeEMPFireState() )
+ return;
+
+ // player "shoot" animation
+ PlayAttackAnimation( ACT_VM_THROW );
+
+ ThrowGrenade();
+
+ // Setup for refire
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+ CheckRemoveDisguise();
+
+ // If I'm now out of ammo, switch away
+ if ( !HasPrimaryAmmo() )
+ {
+ pPlayer->SelectLastItem();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatBaseGrenade::ThrowGrenade( void )
+{
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ BaseClass::WeaponSound(WPN_DOUBLE);
+
+ // Calculate launch velocity (3 seconds for max distance)
+ float flThrowTime = MIN( (gpGlobals->curtime - m_flStartedThrowAt), 3.0 );
+ float flSpeed = 650 + (175 * flThrowTime);
+
+ // If the player's crouched, roll the grenade
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ // Launch the grenade
+ Vector vecForward;
+ QAngle vecAngles = pPlayer->EyeAngles();
+ // Throw it up just a tad
+ vecAngles.x = -1;
+ AngleVectors( vecAngles, &vecForward, NULL, NULL);
+ Vector vecOrigin;
+ VectorLerp( pPlayer->EyePosition(), pPlayer->GetAbsOrigin(), 0.25f, vecOrigin );
+ vecOrigin += (vecForward * 16);
+ vecForward = vecForward * flSpeed;
+ CreateGrenade(vecOrigin, vecForward, pPlayer );
+ }
+ else
+ {
+ // Launch the grenade
+ Vector vecForward;
+ QAngle vecAngles = pPlayer->EyeAngles();
+ AngleVectors( vecAngles, &vecForward, NULL, NULL);
+ Vector vecOrigin = pPlayer->EyePosition();
+ vecOrigin += (vecForward * 16);
+ vecForward = vecForward * flSpeed;
+ CreateGrenade(vecOrigin, vecForward, pPlayer );
+ }
+
+ pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
+}
diff --git a/game/shared/tf2/weapon_combat_basegrenade.h b/game/shared/tf2/weapon_combat_basegrenade.h
new file mode 100644
index 0000000..83fd358
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_basegrenade.h
@@ -0,0 +1,66 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_COMBAT_BASEGRENADE_H
+#define WEAPON_COMBAT_BASEGRENADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "weapon_combat_usedwithshieldbase.h"
+#include "basegrenade_shared.h"
+
+#if defined( CLIENT_DLL )
+#define CWeaponCombatBaseGrenade C_WeaponCombatBaseGrenade
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponCombatBaseGrenade : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponCombatBaseGrenade, CWeaponCombatUsedWithShieldBase );
+public:
+ CWeaponCombatBaseGrenade();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual float GetFireRate( void );
+ virtual void ThrowGrenade( void );
+
+ // Custom grenade types
+ virtual CBaseGrenade *CreateGrenade( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner ) { return NULL; }
+
+ /*
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+ */
+
+public:
+ CNetworkVar( float, m_flStartedThrowAt );
+
+private:
+ CWeaponCombatBaseGrenade( const CWeaponCombatBaseGrenade & );
+};
+
+#endif // WEAPON_COMBAT_BASEGRENADE_H
diff --git a/game/shared/tf2/weapon_combat_burstrifle.cpp b/game/shared/tf2/weapon_combat_burstrifle.cpp
new file mode 100644
index 0000000..ca9c3c3
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_burstrifle.cpp
@@ -0,0 +1,327 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Burst rifle & Shield combo
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combatshield.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "in_buttons.h"
+#include "plasmaprojectile.h"
+#include "IEffects.h"
+
+#if defined( CLIENT_DLL )
+#include "fx.h"
+
+#define CWeaponCombatBurstRifle C_WeaponCombatBurstRifle
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+// Damage CVars
+ConVar weapon_combat_burstrifle_damage( "weapon_combat_burstrifle_damage","10", FCVAR_REPLICATED, "Burst Rifle damage" );
+ConVar weapon_combat_burstrifle_range( "weapon_combat_burstrifle_range","500", FCVAR_REPLICATED, "Burst Rifle maximum range" );
+ConVar weapon_combat_burstrifle_ducking_mod( "weapon_combat_burstrifle_ducking_mod", "0.75", FCVAR_REPLICATED, "Burst Rifle ducking speed modifier" );
+
+#define MAX_RIFLE_POWER 3.0
+#define RIFLE_CHARGE_TIME 2.0
+#define BURSTRIFLE_BOOSTED_FIRERATE 0.015f
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponCombatBurstRifle : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponCombatBurstRifle, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatBurstRifle( void );
+
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual float GetFireRate( void );
+ virtual float GetDefaultAnimSpeed( void );
+ virtual const Vector& GetBulletSpread( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+private:
+ CWeaponCombatBurstRifle( const CWeaponCombatBurstRifle & );
+
+public:
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() &&
+ GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ void GetViewmodelBoneControllers( C_BaseViewModel *pViewModel, float controllers[MAXSTUDIOBONECTRLS]);
+ void ViewModelDrawn( C_BaseViewModel *pViewModel );
+
+private:
+
+ void BoostedMuzzleFlash( C_BaseViewModel *pViewModel, const Vector &vecOrigin, const QAngle &angle, float flScale );
+
+ struct model_t *m_pSpriteBurstRifleFlash[5];
+
+#endif
+};
+
+CWeaponCombatBurstRifle::CWeaponCombatBurstRifle( void )
+{
+ SetPredictionEligible( true );
+}
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatBurstRifle, DT_WeaponCombatBurstRifle )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatBurstRifle, DT_WeaponCombatBurstRifle )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatBurstRifle )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_combat_burstrifle, CWeaponCombatBurstRifle );
+PRECACHE_WEAPON_REGISTER(weapon_combat_burstrifle);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatBurstRifle::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ if ( UsesClipsForAmmo1() )
+ {
+ CheckReload();
+ }
+
+ // Handle firing
+ if ( GetShieldState() == SS_DOWN && !m_bInReload )
+ {
+ if ( (pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ if ( m_iClip1 > 0 )
+ {
+ // Fire the plasma shot
+ PrimaryAttack();
+ }
+ else
+ {
+ Reload();
+ }
+ }
+
+ // Reload button (or fire button when we're out of ammo)
+ if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ if ( pOwner->m_nButtons & IN_RELOAD )
+ {
+ Reload();
+ }
+ else if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ if ( !m_iClip1 && HasPrimaryAmmo() )
+ {
+ Reload();
+ }
+ }
+ }
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're charging
+ AllowShieldPostFrame( m_flNextPrimaryAttack <= gpGlobals->curtime || m_bInReload );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the accuracy derived from weapon and player, and return it
+//-----------------------------------------------------------------------------
+const Vector& CWeaponCombatBurstRifle::GetBulletSpread( void )
+{
+ static Vector cone = VECTOR_CONE_5DEGREES;
+ return cone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatBurstRifle::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ WeaponSound(SINGLE);
+
+ // Fire the bullets
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecSpread = GetBulletSpread();
+ Vector vecAiming, vecRight, vecUp;
+ pPlayer->EyeVectors( &vecAiming, &vecRight, &vecUp );
+
+ // Add some inaccuracy
+ int seed = 0;
+ float x, y, z;
+ do
+ {
+ float x1, x2, y1, y2;
+
+ // Note the additional seed because otherwise we get the same set of random #'s and will get stuck
+ // in an infinite loop here potentially
+ // FIXME: Can we use a gaussian random # function instead? ywb
+ x1 = SHARED_RANDOMFLOAT_SEED( -0.5, 0.5, ++seed );
+ x2 = SHARED_RANDOMFLOAT_SEED( -0.5, 0.5, ++seed );
+ y1 = SHARED_RANDOMFLOAT_SEED( -0.5, 0.5, ++seed );
+ y2 = SHARED_RANDOMFLOAT_SEED( -0.5, 0.5, ++seed );
+
+ x = x1 + x2;
+ y = y1 + y2;
+
+ z = x*x+y*y;
+ } while (z > 1);
+ Vector vecDir = vecAiming + x * vecSpread.x * vecRight + y * vecSpread.y * vecUp;
+
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ // Shift it down a bit so the firer can see it
+ Vector right, forward;
+ AngleVectors( pPlayer->EyeAngles() + pPlayer->m_Local.m_vecPunchAngle, &forward, &right, NULL );
+ Vector vecStartSpot = vecSrc;
+
+ // Get the firing position
+#ifdef CLIENT_DLL
+ // On our client, grab the viewmodel's firing position
+ Vector vecWorldOffset = vecStartSpot + Vector(0,0,-8) + right * 12 + forward * 16;
+#else
+ // For everyone else, grab the weapon model's position
+ /*
+ Vector vecWorldOffset;
+ QAngle angIgnore;
+ GetAttachment( LookupAttachment( "muzzle" ), vecWorldOffset, angIgnore );
+ */
+
+ Vector vecWorldOffset = vecStartSpot + Vector(0,0,-8) + right * 12 + forward * 16;
+#endif
+ Vector gunOffset = vecWorldOffset - vecStartSpot;
+
+ CPowerPlasmaProjectile *pPlasma = CPowerPlasmaProjectile::CreatePredicted( vecStartSpot, vecDir, gunOffset, DMG_ENERGYBEAM, pPlayer );
+ if ( pPlasma )
+ {
+ pPlasma->SetDamage( weapon_combat_burstrifle_damage.GetFloat() );
+ pPlasma->m_hOwner = pPlayer;
+ pPlasma->SetPower( 2.0 );
+ pPlasma->SetMaxRange( weapon_combat_burstrifle_range.GetFloat() );
+ pPlasma->Activate();
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_iClip1 = m_iClip1 - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponCombatBurstRifle::GetFireRate( void )
+{
+ if ( !inv_demo.GetFloat() )
+ {
+ float flFireRate = ( SequenceDuration() * 0.6f ) + SHARED_RANDOMFLOAT( 0.0, 0.035f );
+
+ CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( GetOwner() );
+ if ( pPlayer )
+ {
+ // Ducking players should fire more rapidly.
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ flFireRate *= weapon_combat_burstrifle_ducking_mod.GetFloat();
+ }
+ }
+
+ return flFireRate;
+ }
+
+ // Get the player and check to see if we are powered up.
+ CBaseTFPlayer *pPlayer = ( CBaseTFPlayer* )GetOwner();
+ if ( pPlayer && pPlayer->HasPowerup( POWERUP_BOOST ) )
+ {
+ return BURSTRIFLE_BOOSTED_FIRERATE;
+ }
+
+ return SHARED_RANDOMFLOAT( 0.075f, 0.15f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Match the anim speed to the weapon speed while crouching
+//-----------------------------------------------------------------------------
+float CWeaponCombatBurstRifle::GetDefaultAnimSpeed( void )
+{
+ if ( GetOwner() && GetOwner()->IsPlayer() )
+ {
+ if ( GetOwner()->GetFlags() & FL_DUCKING )
+ return (1.0 + (1.0 - weapon_combat_burstrifle_ducking_mod.GetFloat()) );
+ }
+
+ return 1.0;
+}
+
+#if defined ( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatBurstRifle::GetViewmodelBoneControllers( C_BaseViewModel *pViewModel,
+ float controllers[MAXSTUDIOBONECTRLS])
+{
+ float flAmmoCount;
+ C_BaseTFPlayer *pPlayer = ( C_BaseTFPlayer* )GetOwner();
+ if ( pPlayer && pPlayer->IsDamageBoosted() )
+ {
+ flAmmoCount = random->RandomFloat( 0.0f, 1.0f );
+ }
+ else
+ {
+ // Dial shows ammo count!
+ flAmmoCount = ( float )m_iClip1 / ( float )GetMaxClip1();
+ }
+
+ // Add some shake
+ flAmmoCount += RandomFloat( -0.02, 0.02 );
+ controllers[0] = flAmmoCount;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatBurstRifle::ViewModelDrawn( C_BaseViewModel *pViewModel )
+{
+ C_BaseTFPlayer *pPlayer = ( C_BaseTFPlayer* )GetOwner();
+ if ( pPlayer && pPlayer->IsDamageBoosted() )
+ {
+ Vector vecBarrelPos;
+ QAngle angMuzzle;
+ int iAttachment = pViewModel->LookupAttachment( "muzzle" );
+ pViewModel->GetAttachment( iAttachment, vecBarrelPos, angMuzzle );
+
+ unsigned char color[3];
+ color[0] = 50;
+ color[1] = 128;
+ color[2] = 50;
+ FX_Smoke( vecBarrelPos, angMuzzle, 0.5, 1, &color[0], 192 );
+ }
+}
+#endif
diff --git a/game/shared/tf2/weapon_combat_chargeableplasma.cpp b/game/shared/tf2/weapon_combat_chargeableplasma.cpp
new file mode 100644
index 0000000..3507030
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_chargeableplasma.cpp
@@ -0,0 +1,322 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Chargeable Plasma & Shield combo
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "weapon_combatshield.h"
+#include "tf_guidedplasma.h"
+#include "in_buttons.h"
+#include "tf_gamerules.h"
+
+#define BURST_FIRE_RATE 0.15
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponCombat_ChargeablePlasma : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponCombat_ChargeablePlasma, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_SERVERCLASS();
+
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual float GetFireRate( void );
+ virtual void Spawn();
+ virtual bool Deploy( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ virtual void Precache( void );
+ virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
+ virtual CBaseEntity *GetLockTarget( void );
+
+private:
+ CNetworkVar( bool, m_bCharging );
+ float m_flChargeStartTime;
+ float m_flPower;
+ float m_flNextBurstShotTime;
+ int m_iBurstShotsRemaining;
+ bool m_bHasBurstShot;
+ bool m_bHasCharge;
+
+ // Guidance
+ EHANDLE m_hLockTarget;
+ Vector m_vecTargetOffset;
+ float m_flLockedAt;
+};
+
+IMPLEMENT_SERVERCLASS_ST(CWeaponCombat_ChargeablePlasma, DT_WeaponCombat_ChargeablePlasma )
+ SendPropInt( SENDINFO( m_bCharging ), 1, SPROP_UNSIGNED ),
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_combat_chargeableplasma, CWeaponCombat_ChargeablePlasma );
+PRECACHE_WEAPON_REGISTER(weapon_combat_chargeableplasma);
+
+
+//-----------------------------------------------------------------------------
+// Spawn weapon
+//-----------------------------------------------------------------------------
+void CWeaponCombat_ChargeablePlasma::Spawn()
+{
+ BaseClass::Spawn();
+ m_bHasBurstShot = false;
+ m_bHasCharge = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombat_ChargeablePlasma::Precache( void )
+{
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// New technologies:
+//-----------------------------------------------------------------------------
+void CWeaponCombat_ChargeablePlasma::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+ BaseClass::GainedNewTechnology( pTechnology );
+
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( (CBaseEntity*)GetOwner() );
+ if ( pPlayer )
+ {
+ // Charge-up mode?
+ if ( pPlayer->HasNamedTechnology( "com_comboshield_charge" ) )
+ {
+ m_bHasCharge = true;
+ }
+ else
+ {
+ m_bHasCharge = false;
+ }
+
+ // Burst shot mode?
+ if ( pPlayer->HasNamedTechnology( "com_comboshield_tripleshot" ) )
+ {
+ m_bHasBurstShot = true;
+ }
+ else
+ {
+ m_bHasBurstShot = false;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombat_ChargeablePlasma::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ if ( UsesClipsForAmmo1() )
+ {
+ CheckReload();
+ }
+
+ // If burst shots are firing, ignore input
+ if ( m_iBurstShotsRemaining > 0 )
+ {
+ if ( gpGlobals->curtime < m_flNextBurstShotTime )
+ return;
+
+ if ( m_iClip1 > 0 )
+ {
+ PrimaryAttack();
+ }
+
+ m_iBurstShotsRemaining--;
+ m_flNextBurstShotTime = gpGlobals->curtime + BURST_FIRE_RATE;
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ return;
+ }
+
+ // Handle charge firing
+ if ( m_iClip1 > 0 && GetShieldState() == SS_DOWN && !m_bInReload )
+ {
+ if ( (pOwner->m_nButtons & IN_ATTACK ) )
+ {
+ if (m_bHasCharge)
+ {
+ if ( !m_bCharging && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ m_bCharging = true;
+ m_flChargeStartTime = gpGlobals->curtime;
+
+ // Get a lock target right now
+ m_hLockTarget = GetLockTarget();
+ }
+ }
+ else
+ {
+ // Fire the plasma shot
+ if (m_flNextPrimaryAttack <= gpGlobals->curtime)
+ PrimaryAttack();
+ }
+ }
+ else if ( m_bCharging )
+ {
+ m_bCharging = false;
+
+ // Fire the plasma shot
+ PrimaryAttack();
+
+ // We might be firing a burst shot
+ if (m_bHasBurstShot)
+ {
+ if ( m_flPower >= (MAX_CHARGED_TIME * 0.5) )
+ {
+ if ( m_flPower >= MAX_CHARGED_TIME )
+ {
+ m_iBurstShotsRemaining = 2;
+ }
+ else
+ {
+ m_iBurstShotsRemaining = 1;
+ }
+
+ m_flNextBurstShotTime = gpGlobals->curtime + BURST_FIRE_RATE;
+ }
+ }
+ }
+ }
+
+ // Reload button
+ if ( m_iBurstShotsRemaining == 0 && !m_bCharging )
+ {
+ if ( pOwner->m_nButtons & IN_RELOAD && UsesClipsForAmmo1() && !m_bInReload )
+ {
+ Reload();
+ }
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're charging
+ AllowShieldPostFrame( !m_bCharging && ((m_flNextPrimaryAttack <= gpGlobals->curtime) || m_bInReload) );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombat_ChargeablePlasma::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ WeaponSound(SINGLE);
+
+ // Fire the bullets
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+
+ // If we already have a lock target from button down, see if we shouldn't try and get a new one
+ // Only do this is the button was released immediately
+ if ( !m_hLockTarget || ( m_flLockedAt < gpGlobals->curtime ) )
+ {
+ m_hLockTarget = GetLockTarget();
+ }
+
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ // Shift it down a bit so the firer can see it
+ Vector right;
+ AngleVectors( pPlayer->EyeAngles() + pPlayer->m_Local.m_vecPunchAngle, NULL, &right, NULL );
+ Vector vecStartSpot = vecSrc + Vector(0,0,-8) + right * 12;
+
+ CGuidedPlasma *pShot = CGuidedPlasma::Create(vecStartSpot, vecAiming, m_hLockTarget, m_vecTargetOffset, pPlayer);
+
+ // Set it's charged power level
+ if (m_bHasCharge)
+ m_flPower = MIN( MAX_CHARGED_TIME, gpGlobals->curtime - m_flChargeStartTime );
+ else
+ m_flPower = 0.0f;
+
+ float flDamageMult = RemapVal( m_flPower, 0, MAX_CHARGED_TIME, 1.0, MAX_CHARGED_POWER );
+ pShot->SetPowerLevel( flDamageMult );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_iClip1 = m_iClip1 - 1;
+ m_hLockTarget = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponCombat_ChargeablePlasma::GetFireRate( void )
+{
+ return SequenceDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponCombat_ChargeablePlasma::Deploy( void )
+{
+ if ( BaseClass::Deploy() )
+ {
+ GainedNewTechnology(NULL);
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Our player just died
+//-----------------------------------------------------------------------------
+bool CWeaponCombat_ChargeablePlasma::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ bool bReturn = BaseClass::Holster(pSwitchingTo);
+
+ // Stop the charging sound
+ if ( m_bCharging )
+ {
+ m_bCharging = false;
+ }
+
+ return bReturn;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Try and find an entity to lock onto
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponCombat_ChargeablePlasma::GetLockTarget( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if ( !pPlayer )
+ return NULL;
+
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+ Vector vecEnd = vecSrc + vecAiming * MAX_TRACE_LENGTH;
+
+ trace_t tr;
+ TFGameRules()->WeaponTraceLine( vecSrc, vecEnd, MASK_SHOT, pPlayer, GetDamageType(), &tr );
+
+ if ( (tr.fraction < 1.0f) && tr.m_pEnt )
+ {
+ CBaseEntity *pTargetEntity = tr.m_pEnt;
+
+ // Don't guide on same team or on anything other than players, objects, and NPCs
+ if ( pTargetEntity->InSameTeam(pPlayer) || (!pTargetEntity->IsPlayer()
+ && (pTargetEntity->MyNPCPointer() == NULL)) )
+ return NULL;
+
+ // Compute the target offset relative to the target
+ Vector vecWorldOffset;
+ VectorSubtract( tr.endpos, pTargetEntity->GetAbsOrigin(), vecWorldOffset );
+ VectorIRotate( vecWorldOffset, pTargetEntity->EntityToWorldTransform(), m_vecTargetOffset );
+ m_flLockedAt = gpGlobals->curtime + 0.2;
+ return pTargetEntity;
+ }
+
+ return NULL;
+}
diff --git a/game/shared/tf2/weapon_combat_grenade.cpp b/game/shared/tf2/weapon_combat_grenade.cpp
new file mode 100644
index 0000000..781b710
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_grenade.cpp
@@ -0,0 +1,97 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The Commando's anti-personnel grenades
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "weapon_combat_basegrenade.h"
+#include "weapon_combatshield.h"
+#include "in_buttons.h"
+
+#if !defined( CLIENT_DLL )
+#include "grenade_antipersonnel.h"
+#else
+#define CWeaponCombatGrenade C_WeaponCombatGrenade
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+class CBaseGrenade;
+
+//-----------------------------------------------------------------------------
+// Purpose: Combo shield & grenade weapon
+//-----------------------------------------------------------------------------
+class CWeaponCombatGrenade : public CWeaponCombatBaseGrenade
+{
+ DECLARE_CLASS( CWeaponCombatGrenade, CWeaponCombatBaseGrenade );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatGrenade();
+
+ virtual void Precache( void );
+ virtual CBaseGrenade *CreateGrenade( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+private:
+ CWeaponCombatGrenade( const CWeaponCombatGrenade & );
+
+};
+
+CWeaponCombatGrenade::CWeaponCombatGrenade( void )
+{
+ SetPredictionEligible( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatGrenade::Precache( void )
+{
+ BaseClass::Precache();
+#if !defined( CLIENT_DLL )
+ UTIL_PrecacheOther( "grenade_antipersonnel" );
+#endif
+}
+
+CBaseGrenade *CWeaponCombatGrenade::CreateGrenade( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner )
+{
+#if !defined( CLIENT_DLL )
+ return CGrenadeAntiPersonnel::Create(vecOrigin, vecAngles, pOwner );
+#else
+ return NULL;
+#endif
+}
+
+
+LINK_ENTITY_TO_CLASS( weapon_combat_grenade, CWeaponCombatGrenade );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatGrenade, DT_WeaponCombatGrenade )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatGrenade, DT_WeaponCombatGrenade )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatGrenade )
+END_PREDICTION_DATA()
+
+PRECACHE_WEAPON_REGISTER(weapon_combat_grenade);
diff --git a/game/shared/tf2/weapon_combat_grenade_emp.cpp b/game/shared/tf2/weapon_combat_grenade_emp.cpp
new file mode 100644
index 0000000..feb6062
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_grenade_emp.cpp
@@ -0,0 +1,92 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The Commando's anti-personnel grenades
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "Sprite.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "weapon_combat_basegrenade.h"
+#include "weapon_combatshield.h"
+#include "in_buttons.h"
+#include "grenade_emp.h"
+
+
+#if defined( CLIENT_DLL )
+
+#define CWeaponCombatGrenadeEMP C_WeaponCombatGrenadeEMP
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Combo shield & grenade weapon
+//-----------------------------------------------------------------------------
+class CWeaponCombatGrenadeEMP : public CWeaponCombatBaseGrenade
+{
+ DECLARE_CLASS( CWeaponCombatGrenadeEMP, CWeaponCombatBaseGrenade );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatGrenadeEMP();
+
+ virtual void Precache( void );
+ virtual CBaseGrenade *CreateGrenade( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+private:
+ CWeaponCombatGrenadeEMP( const CWeaponCombatGrenadeEMP & );
+
+};
+
+LINK_ENTITY_TO_CLASS( weapon_combat_grenade_emp, CWeaponCombatGrenadeEMP );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatGrenadeEMP, DT_WeaponCombatGrenadeEMP )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatGrenadeEMP, DT_WeaponCombatGrenadeEMP )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatGrenadeEMP )
+END_PREDICTION_DATA()
+
+PRECACHE_WEAPON_REGISTER(weapon_combat_grenade_emp);
+
+CWeaponCombatGrenadeEMP::CWeaponCombatGrenadeEMP( void )
+{
+ SetPredictionEligible( true );
+}
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatGrenadeEMP::Precache( void )
+{
+ BaseClass::Precache();
+#if !defined( CLIENT_DLL )
+ UTIL_PrecacheOther( "grenade_emp" );
+#endif
+}
+
+CBaseGrenade *CWeaponCombatGrenadeEMP::CreateGrenade( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner )
+{
+ return CGrenadeEMP::Create(vecOrigin, vecAngles, pOwner );
+}
diff --git a/game/shared/tf2/weapon_combat_laserrifle.cpp b/game/shared/tf2/weapon_combat_laserrifle.cpp
new file mode 100644
index 0000000..e751686
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_laserrifle.cpp
@@ -0,0 +1,375 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Laser Rifle & Shield combo
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "weapon_combatshield.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "in_buttons.h"
+#include "takedamageinfo.h"
+#include "beam_shared.h"
+#include "tf_gamerules.h"
+
+// Damage CVars
+ConVar weapon_combat_laserrifle_damage( "weapon_combat_laserrifle_damage","20", FCVAR_REPLICATED, "Laser rifle damage" );
+ConVar weapon_combat_laserrifle_range( "weapon_combat_laserrifle_range","1000", FCVAR_REPLICATED, "Laser rifle maximum range" );
+ConVar weapon_combat_laserrifle_ducking_mod( "weapon_combat_laserrifle_ducking_mod", "0.75", FCVAR_REPLICATED, "Laser rifle ducking ROF modifier" );
+
+#if defined( CLIENT_DLL )
+#include "fx.h"
+#include "hud.h"
+#include "c_te_effect_dispatch.h"
+#include <vgui/ISurface.h>
+
+#define CWeaponCombatLaserRifle C_WeaponCombatLaserRifle
+
+#else
+
+#include "te_effect_dispatch.h"
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponCombatLaserRifle : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponCombatLaserRifle, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatLaserRifle( void );
+
+ virtual const Vector& GetBulletSpread( void );
+ virtual void ItemBusyFrame( void );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual float GetFireRate( void );
+ virtual float GetDefaultAnimSpeed( void );
+ virtual void BulletWasFired( const Vector &vecStart, const Vector &vecEnd );
+
+ void RecalculateAccuracy( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+private:
+ CWeaponCombatLaserRifle( const CWeaponCombatLaserRifle & );
+
+public:
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() &&
+ GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+ virtual void DrawCrosshair( void );
+#endif
+
+private:
+ float m_flInaccuracy;
+ float m_flAccuracyTime;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponCombatLaserRifle::CWeaponCombatLaserRifle( void )
+{
+ SetPredictionEligible( true );
+ m_flInaccuracy = 0;
+ m_flAccuracyTime = 0;
+}
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatLaserRifle, DT_WeaponCombatLaserRifle )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatLaserRifle, DT_WeaponCombatLaserRifle )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatLaserRifle )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_combat_laserrifle, CWeaponCombatLaserRifle );
+PRECACHE_WEAPON_REGISTER(weapon_combat_laserrifle);
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the accuracy derived from weapon and player, and return it
+//-----------------------------------------------------------------------------
+const Vector& CWeaponCombatLaserRifle::GetBulletSpread( void )
+{
+ static Vector cone = VECTOR_CONE_8DEGREES;
+ return cone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatLaserRifle::ItemBusyFrame( void )
+{
+ BaseClass::ItemBusyFrame();
+
+ RecalculateAccuracy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatLaserRifle::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ if ( UsesClipsForAmmo1() )
+ {
+ CheckReload();
+ }
+
+ RecalculateAccuracy();
+
+ // Handle firing
+ if ( GetShieldState() == SS_DOWN && !m_bInReload )
+ {
+ if ( (pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ if ( m_iClip1 > 0 )
+ {
+ // Fire the plasma shot
+ PrimaryAttack();
+ }
+ else
+ {
+ Reload();
+ }
+ }
+
+ // Reload button (or fire button when we're out of ammo)
+ if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ if ( pOwner->m_nButtons & IN_RELOAD )
+ {
+ Reload();
+ }
+ else if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ if ( !m_iClip1 && HasPrimaryAmmo() )
+ {
+ Reload();
+ }
+ }
+ }
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're charging
+ AllowShieldPostFrame( m_flNextPrimaryAttack <= gpGlobals->curtime || m_bInReload );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatLaserRifle::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ WeaponSound(SINGLE);
+
+ // Fire the bullets
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ // Reduce the spread if the player's ducking
+ Vector vecSpread = GetBulletSpread();
+ vecSpread *= m_flInaccuracy;
+
+ TFGameRules()->FireBullets( CTakeDamageInfo( this, pPlayer, weapon_combat_laserrifle_damage.GetFloat(), DMG_PLASMA), 1,
+ vecSrc, vecAiming, vecSpread, weapon_combat_laserrifle_range.GetFloat(), m_iPrimaryAmmoType, 0, entindex(), 0 );
+
+ m_flInaccuracy += 0.3;
+ m_flInaccuracy = clamp(m_flInaccuracy, 0, 1);
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_iClip1 = m_iClip1 - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponCombatLaserRifle::GetFireRate( void )
+{
+ float flFireRate = ( SequenceDuration() * 0.4 ) + SHARED_RANDOMFLOAT( 0.0, 0.035f );
+
+ CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( GetOwner() );
+ if ( pPlayer )
+ {
+ // Ducking players should fire more rapidly.
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ flFireRate *= weapon_combat_laserrifle_ducking_mod.GetFloat();
+ }
+ }
+
+ return flFireRate;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatLaserRifle::RecalculateAccuracy( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ m_flAccuracyTime += gpGlobals->frametime;
+
+ while ( m_flAccuracyTime > 0.05 )
+ {
+ if ( !(pPlayer->GetFlags() & FL_ONGROUND) )
+ {
+ m_flInaccuracy += 0.05;
+ }
+ else if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ m_flInaccuracy -= 0.08;
+ }
+/*
+ else if ( pPlayer->GetLocalVelocity().LengthSqr() > (100*100) )
+ {
+ // Never get worse than 1/2 accuracy from running
+ if ( m_flInaccuracy < 0.25 )
+ {
+ m_flInaccuracy += 0.01;
+ if ( m_flInaccuracy > 0.5 )
+ {
+ m_flInaccuracy = 0.5;
+ }
+ }
+ else if ( m_flInaccuracy > 0.25 )
+ {
+ m_flInaccuracy -= 0.01;
+ }
+ }
+*/
+ else
+ {
+ m_flInaccuracy -= 0.04;
+ }
+
+ // Crouching prevents accuracy ever going beyond a point
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ m_flInaccuracy = clamp(m_flInaccuracy, 0, 0.8);
+ }
+ else
+ {
+ m_flInaccuracy = clamp(m_flInaccuracy, 0, 1);
+ }
+
+ m_flAccuracyTime -= 0.05;
+
+#ifndef CLIENT_DLL
+ //if ( m_flInaccuracy )
+ //Msg("Inaccuracy %.2f (%.2f)\n", m_flInaccuracy, gpGlobals->curtime );
+#endif
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Match the anim speed to the weapon speed while crouching
+//-----------------------------------------------------------------------------
+float CWeaponCombatLaserRifle::GetDefaultAnimSpeed( void )
+{
+ if ( GetOwner() && GetOwner()->IsPlayer() )
+ {
+ if ( GetOwner()->GetFlags() & FL_DUCKING )
+ return (1.0 + (1.0 - weapon_combat_laserrifle_ducking_mod.GetFloat()) );
+ }
+
+ return 1.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the laser rifle effect
+//-----------------------------------------------------------------------------
+void CWeaponCombatLaserRifle::BulletWasFired( const Vector &vecStart, const Vector &vecEnd )
+{
+ // Humans fire jazzed up bullets, Aliens fire laserbeams
+ if ( GetTeamNumber() == TEAM_HUMANS )
+ {
+ UTIL_Tracer( (Vector&)vecStart, (Vector&)vecEnd, entindex(), 1, 5000, false, "HLaserTracer" );
+ }
+ else
+ {
+ UTIL_Tracer( (Vector&)vecStart, (Vector&)vecEnd, entindex(), 1, 5000, false, "ALaserTracer" );
+ }
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Draw the weapon's crosshair
+//-----------------------------------------------------------------------------
+void CWeaponCombatLaserRifle::DrawCrosshair( void )
+{
+ BaseClass::DrawCrosshair();
+
+ // Draw the targeting zone around the crosshair
+ int r, g, b, a;
+ gHUD.m_clrYellowish.GetColor( r, g, b, a );
+
+ // Check to see if we are in vgui mode
+ C_BaseTFPlayer *pPlayer = static_cast<C_BaseTFPlayer*>( GetOwner() );
+ if ( !pPlayer || pPlayer->IsInVGuiInputMode() )
+ return;
+
+ // Draw a crosshair & accuracy hoodad
+ int iBarWidth = XRES(10);
+ int iBarHeight = YRES(10);
+ int iTotalWidth = (iBarWidth * 2) + (40 * m_flInaccuracy) + XRES(10);
+ int iTotalHeight = (iBarHeight * 2) + (40 * m_flInaccuracy) + YRES(10);
+
+ // Horizontal bars
+ int iLeft = (ScreenWidth() - iTotalWidth) / 2;
+ int iMidHeight = (ScreenHeight() / 2);
+
+ Color dark( r, g, b, 32 );
+ Color light( r, g, b, 160 );
+
+ vgui::surface()->DrawSetColor( dark );
+
+ vgui::surface()->DrawFilledRect( iLeft, iMidHeight-1, iLeft+ iBarWidth, iMidHeight + 2 );
+ vgui::surface()->DrawFilledRect( iLeft + iTotalWidth - iBarWidth, iMidHeight-1, iLeft + iTotalWidth, iMidHeight + 2 );
+
+ vgui::surface()->DrawSetColor( light );
+
+ vgui::surface()->DrawFilledRect( iLeft, iMidHeight, iLeft + iBarWidth, iMidHeight + 1 );
+ vgui::surface()->DrawFilledRect( iLeft + iTotalWidth - iBarWidth, iMidHeight, iLeft + iTotalWidth, iMidHeight + 1 );
+
+ // Vertical bars
+ int iTop = (ScreenHeight() - iTotalHeight) / 2;
+ int iMidWidth = (ScreenWidth() / 2);
+
+ vgui::surface()->DrawSetColor( dark );
+
+ vgui::surface()->DrawFilledRect( iMidWidth-1, iTop, iMidWidth + 2, iTop + iBarHeight );
+ vgui::surface()->DrawFilledRect( iMidWidth-1, iTop + iTotalHeight - iBarHeight, iMidWidth + 2, iTop + iTotalHeight );
+
+ vgui::surface()->DrawSetColor( light );
+
+ vgui::surface()->DrawFilledRect( iMidWidth, iTop, iMidWidth + 1, iTop + iBarHeight );
+ vgui::surface()->DrawFilledRect( iMidWidth, iTop + iTotalHeight - iBarHeight, iMidWidth + 1, iTop + iTotalHeight );
+}
+#endif \ No newline at end of file
diff --git a/game/shared/tf2/weapon_combat_plasma_grenade_launcher.cpp b/game/shared/tf2/weapon_combat_plasma_grenade_launcher.cpp
new file mode 100644
index 0000000..17431ad
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_plasma_grenade_launcher.cpp
@@ -0,0 +1,195 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Burst rifle & Shield combo
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combatshield.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "in_buttons.h"
+#include "plasmaprojectile.h"
+
+#if !defined( CLIENT_DLL )
+
+#include "grenade_antipersonnel.h"
+
+#else
+
+#define CWeaponCombatPlasmaGrenadeLauncher C_WeaponCombatPlasmaGrenadeLauncher
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+// Damage CVars
+ConVar weapon_combat_plasmagrenadelauncher_damage( "weapon_combat_plasmagrenadelauncher_damage","40", FCVAR_REPLICATED, "Burst Rifle damage" );
+ConVar weapon_combat_plasmagrenadelauncher_radius( "weapon_combat_plasmagrenadelauncher_radius","100", FCVAR_REPLICATED, "Burst Rifle maximum range" );
+
+
+#define MAX_RIFLE_POWER 3.0
+#define RIFLE_CHARGE_TIME 2.0
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponCombatPlasmaGrenadeLauncher : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponCombatPlasmaGrenadeLauncher, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatPlasmaGrenadeLauncher();
+
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual float GetFireRate( void );
+ virtual void Precache( void );
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() &&
+ GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+private:
+ CWeaponCombatPlasmaGrenadeLauncher( const CWeaponCombatPlasmaGrenadeLauncher & );
+
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatPlasmaGrenadeLauncher, DT_WeaponCombatPlasmaGrenadeLauncher )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatPlasmaGrenadeLauncher, DT_WeaponCombatPlasmaGrenadeLauncher )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatPlasmaGrenadeLauncher )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_combat_plasmagrenadelauncher, CWeaponCombatPlasmaGrenadeLauncher );
+PRECACHE_WEAPON_REGISTER(weapon_combat_plasmagrenadelauncher);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponCombatPlasmaGrenadeLauncher::CWeaponCombatPlasmaGrenadeLauncher()
+{
+ m_bReloadsSingly = true;
+ SetPredictionEligible( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaGrenadeLauncher::Precache( void )
+{
+ BaseClass::Precache();
+#if !defined( CLIENT_DLL )
+ UTIL_PrecacheOther( "grenade_antipersonnel" );
+#endif
+ PrecacheModel( "models/weapons/w_grenade.mdl" );
+}
+
+void CWeaponCombatPlasmaGrenadeLauncher::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ if ( UsesClipsForAmmo1() )
+ {
+ CheckReload();
+ }
+
+ // Handle firing
+ if ( GetShieldState() == SS_DOWN && !m_bInReload )
+ {
+ if ( (pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ if ( m_iClip1 > 0 )
+ {
+ // Fire the plasma shot
+ PrimaryAttack();
+ }
+ else
+ {
+ Reload();
+ }
+ }
+
+ // Reload button (or fire button when we're out of ammo)
+ if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ if ( pOwner->m_nButtons & IN_RELOAD )
+ {
+ Reload();
+ }
+ else if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ if ( !m_iClip1 && HasPrimaryAmmo() )
+ {
+ Reload();
+ }
+ }
+ }
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're charging
+ AllowShieldPostFrame( m_flNextPrimaryAttack <= gpGlobals->curtime || m_bInReload );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaGrenadeLauncher::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ WeaponSound(SINGLE);
+
+ // Fire the bullets
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ // Launch the grenade
+ Vector vecForward;
+ pPlayer->EyeVectors( &vecForward );
+ Vector vecOrigin = pPlayer->EyePosition();
+ vecOrigin += (vecForward);
+
+#if !defined( CLIENT_DLL )
+ float flSpeed = 1200;
+
+ CGrenadeAntiPersonnel* pGrenade = CGrenadeAntiPersonnel::Create(vecOrigin, vecForward * flSpeed, pPlayer );
+ pGrenade->SetModel( "models/weapons/w_grenade.mdl" );
+ pGrenade->SetBounceSound( "PlasmaGrenade.Bounce" );
+ pGrenade->SetDamage( weapon_combat_plasmagrenadelauncher_damage.GetFloat() );
+ pGrenade->SetDamageRadius( weapon_combat_plasmagrenadelauncher_radius.GetFloat() );
+ pGrenade->SetExplodeOnContact( true );
+#endif
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_iClip1 = m_iClip1 - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponCombatPlasmaGrenadeLauncher::GetFireRate( void )
+{
+ return SequenceDuration() * 3;
+}
diff --git a/game/shared/tf2/weapon_combat_plasmarifle.cpp b/game/shared/tf2/weapon_combat_plasmarifle.cpp
new file mode 100644
index 0000000..2ede901
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_plasmarifle.cpp
@@ -0,0 +1,559 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Chargeable Plasma & Shield combo
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combatshield.h"
+#include "in_buttons.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "plasmaprojectile.h"
+#include "in_buttons.h"
+#include "tf_shareddefs.h"
+
+#if defined( CLIENT_DLL )
+
+#include "iefx.h"
+#include "dlight.h"
+#include "clienteffectprecachesystem.h"
+#include "beamdraw.h"
+
+#define CWeaponCombatPlasmaRifle C_WeaponCombatPlasmaRifle
+#define CWeaponCombatPlasmaRifleHuman C_WeaponCombatPlasmaRifleHuman
+#define CWeaponCombatPlasmaRifleAlien C_WeaponCombatPlasmaRifleAlien
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#if defined( CLIENT_DLL )
+
+class CChargeBall;
+// Precache the effects
+CLIENTEFFECT_REGISTER_BEGIN( PrecacheWeaponCombatPlasmaRifle )
+CLIENTEFFECT_MATERIAL( "sprites/chargeball_team1" )
+CLIENTEFFECT_MATERIAL( "sprites/chargeball_team2" )
+CLIENTEFFECT_REGISTER_END()
+
+#endif
+
+// Damage CVars
+ConVar weapon_combat_plasmarifle_damage( "weapon_combat_plasmarifle_damage","10", FCVAR_REPLICATED, "Plasma Rifle maximum damage" );
+ConVar weapon_combat_plasmarifle_range( "weapon_combat_plasmarifle_range","500", FCVAR_REPLICATED, "Plasma Rifle maximum range" );
+ConVar weapon_combat_plasmarifle_radius( "weapon_combat_plasmarifle_radius","90", FCVAR_REPLICATED, "Plasma Rifle explosion radius when charged" );
+ConVar weapon_combat_plasmarifle_ducking_mod( "weapon_combat_plasmarifle_ducking_mod", "0.6f", FCVAR_REPLICATED, "Plasma Rifle ducking speed modifier" );
+
+//-----------------------------------------------------------------------------
+// Purpose: Shared viersion of CWeaponCombatPlasmaRifle
+//-----------------------------------------------------------------------------
+class CWeaponCombatPlasmaRifle : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponCombatPlasmaRifle, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+#if !defined( CLIENT_DLL )
+ DECLARE_DATADESC();
+#endif
+
+ CWeaponCombatPlasmaRifle( void ) {}
+
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual float GetFireRate( void );
+ virtual void Spawn();
+ virtual bool Deploy( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ void ChargeThink( void );
+ virtual float GetDefaultAnimSpeed( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+private:
+ CWeaponCombatPlasmaRifle( const CWeaponCombatPlasmaRifle & );
+public:
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() &&
+ GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual int DrawModel( int flags );
+ virtual void ViewModelDrawn( CBaseViewModel *pBaseViewModel );
+ virtual void ClientThink( );
+ virtual bool IsTransparent( );
+private:
+ // Purpose: Draws the charging effect
+ void DrawChargingEffect( float flSize, CBaseAnimating *pAttachedEnt );
+ CMaterialReference m_hMaterial;
+
+#endif
+private:
+ CNetworkVar( float, m_flPower );
+ CNetworkVar( bool, m_bCharging );
+};
+
+//-----------------------------------------------------------------------------
+// Spawn weapon
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaRifle::Spawn()
+{
+ BaseClass::Spawn();
+ m_flPower = 1;
+ m_bCharging = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaRifle::ItemPostFrame( void )
+{
+ ChargeThink();
+
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ if ( UsesClipsForAmmo1() )
+ {
+ CheckReload();
+ }
+
+ // Handle charge firing
+ if ( GetShieldState() == SS_DOWN && !m_bInReload )
+ {
+ if ( (pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ if ( m_iClip1 > 0 )
+ {
+ // Fire the plasma shot
+ PrimaryAttack();
+ m_flPower = 1.0;
+ }
+ else
+ {
+ Reload();
+ }
+ }
+
+ // Reload button (or fire button when we're out of ammo)
+ if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ if ( pOwner->m_nButtons & IN_RELOAD )
+ {
+ Reload();
+ }
+ else if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ if ( !m_iClip1 && HasPrimaryAmmo() )
+ {
+ Reload();
+ }
+ }
+ }
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're charging
+ AllowShieldPostFrame( m_flNextPrimaryAttack <= gpGlobals->curtime || m_bInReload );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaRifle::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ WeaponSound(SINGLE);
+
+ // Fire the bullets
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ // Shift it down a bit so the firer can see it
+ Vector right, forward;
+ AngleVectors( pPlayer->EyeAngles() + pPlayer->m_Local.m_vecPunchAngle, &forward, &right, NULL );
+ Vector vecStartSpot = vecSrc;
+
+ Vector gunOffset = Vector(0,0,-8) + right * 12 + forward * 16;
+
+ CPowerPlasmaProjectile *pPlasma = CPowerPlasmaProjectile::CreatePredicted( vecStartSpot, vecAiming, gunOffset, DMG_ENERGYBEAM, pPlayer );
+ if ( pPlasma )
+ {
+ pPlasma->SetDamage( m_flPower * weapon_combat_plasmarifle_damage.GetFloat() );
+ pPlasma->m_hOwner = pPlayer;
+ pPlasma->SetPower( m_flPower );
+ // Calculate range based upon charge power
+ float flRange = weapon_combat_plasmarifle_range.GetFloat() + RemapVal( m_flPower, 1.0, MAX_RIFLE_POWER, 0, weapon_combat_plasmarifle_range.GetFloat() * 0.75 );
+ pPlasma->SetMaxRange( flRange );
+ pPlasma->Activate();
+ }
+
+ // Go explosive if fully charged
+// if ( m_flPower >= MAX_RIFLE_POWER )
+// {
+// pPlasma->SetExplosive( weapon_combat_plasmarifle_radius.GetFloat() );
+// pPlasma->SetPlasmaType( PLASMATYPE_PLASMABALL_EXPLOSIVE );
+// }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_iClip1 = m_iClip1 - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponCombatPlasmaRifle::GetFireRate( void )
+{
+ float flFireRate = ( SequenceDuration() * 0.4 ) + SHARED_RANDOMFLOAT( 0.0, 0.035f );
+
+ // Get the player.
+ CBaseTFPlayer *pPlayer = ( CBaseTFPlayer* )GetOwner();
+ if ( pPlayer )
+ {
+ // Fire more rapidly when we are ducking.
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ flFireRate *= weapon_combat_plasmarifle_ducking_mod.GetFloat();
+ }
+ }
+
+ return flFireRate;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponCombatPlasmaRifle::Deploy( void )
+{
+ if ( BaseClass::Deploy() )
+ {
+ m_bCharging = true;
+ GainedNewTechnology(NULL);
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Our player just died
+//-----------------------------------------------------------------------------
+bool CWeaponCombatPlasmaRifle::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ if ( BaseClass::Holster(pSwitchingTo) )
+ {
+ m_bCharging = false;
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Match the anim speed to the weapon speed while crouching
+//-----------------------------------------------------------------------------
+float CWeaponCombatPlasmaRifle::GetDefaultAnimSpeed( void )
+{
+ if ( GetOwner() && GetOwner()->IsPlayer() )
+ {
+ if ( GetOwner()->GetFlags() & FL_DUCKING )
+ return (1.0 + (1.0 - weapon_combat_plasmarifle_ducking_mod.GetFloat()) );
+ }
+
+ return 1.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Charge up over time
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaRifle::ChargeThink( void )
+{
+ if ( !m_bCharging )
+ return;
+
+ if ( IsOwnerEMPed() )
+ {
+ m_flPower = 1;
+ }
+ else if ( m_iClip1 > 0 && m_flPower < MAX_RIFLE_POWER )
+ {
+ m_flPower = MIN( MAX_RIFLE_POWER, m_flPower + (((MAX_RIFLE_POWER-1.0) / RIFLE_CHARGE_TIME) * gpGlobals->frametime ) );
+ }
+}
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaRifle::OnDataChanged( DataUpdateType_t updateType )
+{
+ SetPredictionEligible( true );
+
+ BaseClass::OnDataChanged( updateType );
+
+ if (updateType == DATA_UPDATE_CREATED)
+ {
+ if ( GetTeamNumber() == 1 )
+ m_hMaterial.Init( "sprites/chargeball_team1", TEXTURE_GROUP_CLIENT_EFFECTS );
+ else
+ m_hMaterial.Init( "sprites/chargeball_team2", TEXTURE_GROUP_CLIENT_EFFECTS );
+ }
+
+ if (WeaponState() == WEAPON_IS_ACTIVE)
+ {
+ // Start thinking so we can manipulate the light
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+ else
+ {
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Deal with dynamic lighting
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaRifle::ClientThink( )
+{
+ BaseClass::ClientThink();
+
+ if (!inv_demo.GetInt())
+ {
+ C_BaseTFPlayer *pPlayer = (C_BaseTFPlayer *)GetOwner();
+ if ( !pPlayer || (pPlayer->GetHealth() <= 0) || !IsDormant() )
+ {
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ return;
+ }
+
+ // FIXME: dl->origin should be based on the attachment point
+ dlight_t *dl = effects->CL_AllocDlight( entindex() );
+ dl->origin = GetRenderOrigin();
+ if (GetTeamNumber() == 1)
+ {
+ dl->color.r = 40;
+ dl->color.g = 60;
+ dl->color.b = 250;
+ }
+ else
+ {
+ dl->color.r = 250;
+ dl->color.g = 60;
+ dl->color.b = 40;
+ }
+ dl->color.exponent = 7;
+ dl->radius = 20 * m_flPower + 10;
+ dl->die = gpGlobals->curtime + 0.01;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the charging effect
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaRifle::DrawChargingEffect( float flSize, CBaseAnimating *pAttachedEnt )
+{
+ if (!pAttachedEnt)
+ return;
+
+ Vector vecMuzzleOrigin, vecBarrelOrigin;
+ QAngle angMuzzleAngles, angBarrelAngles;
+ int iMuzzle = pAttachedEnt->LookupAttachment( "muzzle" );
+ //int iBarrel = pAttachedEnt->LookupAttachment( "barrel" );
+
+ if ( pAttachedEnt->GetAttachment( iMuzzle, vecMuzzleOrigin, angMuzzleAngles ) )
+ {
+ // View model attachments are modified so you can place entities at the attachment
+ // point and when they are rendered (with a different FOV than the view model itself uses)
+ // they will render in the right spot when the view model is drawn.
+ //
+ // In this case though, we are rendering at the same time as the view model, so we want
+ // the attachment point before the correction has been applied.
+ pAttachedEnt->UncorrectViewModelAttachment( vecMuzzleOrigin );
+
+ // If I'm fully charged, put funky effects on the ball
+ materials->Bind( m_hMaterial, this );
+
+ if ( m_flPower >= MAX_RIFLE_POWER )
+ {
+ float frac = fmod( gpGlobals->curtime, 1.0 );
+ frac *= 2 * M_PI;
+ frac = sin( frac );
+ flSize += (frac * 2) - 1.5;
+ int colorFade = 190 + (int)( frac * 32.0f );
+
+ color32 color = { 0, 0, 0, 255 };
+ if ( GetTeamNumber() == 1 )
+ {
+ color.r = colorFade;
+ color.g = colorFade;
+ }
+ else
+ {
+ color.g = colorFade;
+ }
+ DrawSprite( vecMuzzleOrigin, flSize, flSize, color );
+ }
+ else
+ {
+ color32 color = { 255, 255, 255, 255 };
+ DrawSprite( vecMuzzleOrigin, flSize, flSize, color );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// We're transparent because we draw a transparent charging effect
+//-----------------------------------------------------------------------------
+bool CWeaponCombatPlasmaRifle::IsTransparent( )
+{
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the model
+//-----------------------------------------------------------------------------
+int CWeaponCombatPlasmaRifle::DrawModel( int flags )
+{
+ int retval = BaseClass::DrawModel( flags );
+ if (retval == 0)
+ return 0;
+
+ if (IsCarrierAlive())
+ {
+ // FIXME: Maybe do some client-side simulation on the size?
+ // It may get jerky otherwise
+
+ // Draw the charging effect
+ float flSize = 10 * m_flPower + 5;
+
+ DrawChargingEffect( flSize, this );
+ }
+ return retval;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Draws the model
+//-----------------------------------------------------------------------------
+void CWeaponCombatPlasmaRifle::ViewModelDrawn( CBaseViewModel *pBaseViewModel )
+{
+ // Draw the charging effect
+ float flSize = 4 * m_flPower + 1;
+
+ if ( m_iClip1 > 0 )
+ {
+ DrawChargingEffect( flSize, pBaseViewModel );
+ }
+}
+#endif
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatPlasmaRifle, DT_WeaponCombatPlasmaRifle )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatPlasmaRifle, DT_WeaponCombatPlasmaRifle )
+#if !defined( CLIENT_DLL )
+ SendPropFloat( SENDINFO( m_flPower ), 14, SPROP_ROUNDUP, 1.0f, MAX_RIFLE_POWER ),
+ SendPropInt( SENDINFO( m_bCharging ), 1, SPROP_UNSIGNED ),
+#else
+ RecvPropFloat( RECVINFO( m_flPower ) ),
+ RecvPropInt( RECVINFO( m_bCharging ) ),
+#endif
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_combat_plasmarifle_base, CWeaponCombatPlasmaRifle );
+
+BEGIN_PREDICTION_DATA( CWeaponCombatPlasmaRifle )
+
+ DEFINE_PRED_FIELD_TOL( m_flPower, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.05f ),
+ DEFINE_PRED_FIELD( m_bCharging, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+#if !defined( CLIENT_DLL )
+
+BEGIN_DATADESC( CWeaponCombatPlasmaRifle )
+
+ // Function Pointers
+ DEFINE_FUNCTION( ChargeThink ),
+
+END_DATADESC()
+
+// PRECACHE_WEAPON_REGISTER(weapon_combat_plasmarifle_base);
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Need to do different art on client vs server
+//-----------------------------------------------------------------------------
+class CWeaponCombatPlasmaRifleHuman : public CWeaponCombatPlasmaRifle
+{
+ DECLARE_CLASS( CWeaponCombatPlasmaRifleHuman, CWeaponCombatPlasmaRifle );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatPlasmaRifleHuman( void ) {}
+
+private:
+ CWeaponCombatPlasmaRifleHuman( const CWeaponCombatPlasmaRifleHuman & );
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Need to do different art on client vs server
+//-----------------------------------------------------------------------------
+class CWeaponCombatPlasmaRifleAlien : public CWeaponCombatPlasmaRifle
+{
+ DECLARE_CLASS( CWeaponCombatPlasmaRifleAlien, CWeaponCombatPlasmaRifle );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatPlasmaRifleAlien( void ) {}
+
+private:
+ CWeaponCombatPlasmaRifleAlien( const CWeaponCombatPlasmaRifleAlien & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatPlasmaRifleHuman, DT_WeaponCombatPlasmaRifleHuman )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatPlasmaRifleHuman, DT_WeaponCombatPlasmaRifleHuman )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatPlasmaRifleHuman )
+END_PREDICTION_DATA()
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatPlasmaRifleAlien, DT_WeaponCombatPlasmaRifleAlien )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatPlasmaRifleAlien, DT_WeaponCombatPlasmaRifleAlien )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatPlasmaRifleAlien )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_combat_plasmarifle, CWeaponCombatPlasmaRifleHuman );
+LINK_ENTITY_TO_CLASS( weapon_combat_plasmarifle_alien, CWeaponCombatPlasmaRifleAlien );
+
+PRECACHE_WEAPON_REGISTER(weapon_combat_plasmarifle);
+PRECACHE_WEAPON_REGISTER(weapon_combat_plasmarifle_alien);
diff --git a/game/shared/tf2/weapon_combat_shotgun.cpp b/game/shared/tf2/weapon_combat_shotgun.cpp
new file mode 100644
index 0000000..ac5ddaf
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_shotgun.cpp
@@ -0,0 +1,318 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Shotgun & Shield combo
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "weapon_combatshield.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "in_buttons.h"
+#include "takedamageinfo.h"
+#include "tf_gamerules.h"
+
+// Damage CVars
+ConVar weapon_combat_shotgun_damage( "weapon_combat_shotgun_damage","5", FCVAR_REPLICATED, "Shotgun damage per pellet" );
+ConVar weapon_combat_shotgun_powered_damage( "weapon_combat_shotgun_powered_damage","10", FCVAR_REPLICATED, "Shotgun damage per pellet when powered" );
+ConVar weapon_combat_shotgun_range( "weapon_combat_shotgun_range","900", FCVAR_REPLICATED, "Shotgun maximum range" );
+ConVar weapon_combat_shotgun_pellets( "weapon_combat_shotgun_pellets","8", FCVAR_REPLICATED, "Shotgun pellets per fire" );
+ConVar weapon_combat_shotgun_ducking_mod( "weapon_combat_shotgun_ducking_mod", "0.75", FCVAR_REPLICATED, "Shotgun ducking speed modifier" );
+ConVar weapon_combat_shotgun_energy_cost( "weapon_combat_shotgun_energy_cost", "0.1", FCVAR_REPLICATED, "Sapper's energy cost to fire a powered shotgun round" );
+
+
+#if defined( CLIENT_DLL )
+#include "fx.h"
+#include "c_tf_class_sapper.h"
+#define CWeaponCombatShotgun C_WeaponCombatShotgun
+#define CPlayerClassSapper C_PlayerClassSapper
+#else
+#include "tf_class_sapper.h"
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponCombatShotgun : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponCombatShotgun, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatShotgun( void );
+
+ virtual const Vector& GetBulletSpread( void );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual float GetFireRate( void );
+ virtual float GetDefaultAnimSpeed( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+private:
+ CWeaponCombatShotgun( const CWeaponCombatShotgun & );
+
+public:
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() &&
+ GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+ void ViewModelDrawn( C_BaseViewModel *pViewModel );
+#endif
+
+private:
+ // If true, the current shot being fired is a powered shot, using some of the Sapper's energy
+ bool m_bPoweredShot;
+ float m_flOwnersEnergyLevel;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponCombatShotgun::CWeaponCombatShotgun( void )
+{
+ SetPredictionEligible( true );
+ m_bReloadsSingly = true;
+ m_flOwnersEnergyLevel = 0;
+}
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatShotgun, DT_WeaponCombatShotgun )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatShotgun, DT_WeaponCombatShotgun )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatShotgun )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_combat_shotgun, CWeaponCombatShotgun );
+PRECACHE_WEAPON_REGISTER(weapon_combat_shotgun);
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the accuracy derived from weapon and player, and return it
+//-----------------------------------------------------------------------------
+const Vector& CWeaponCombatShotgun::GetBulletSpread( void )
+{
+ static Vector cone = VECTOR_CONE_20DEGREES;
+ static Vector powered_cone = VECTOR_CONE_10DEGREES;
+
+ if ( m_bPoweredShot )
+ return powered_cone;
+
+ return cone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShotgun::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ // Get our player's energy level
+ if ( pOwner->PlayerClass() == TFCLASS_SAPPER )
+ {
+ CPlayerClassSapper *pSapper = static_cast<CPlayerClassSapper*>( pOwner->GetPlayerClass() );
+ if ( pSapper )
+ {
+ m_flOwnersEnergyLevel = pSapper->GetDrainedEnergy();
+ }
+ }
+
+ if ( UsesClipsForAmmo1() )
+ {
+ CheckReload();
+ }
+
+ // Handle firing
+ if ( GetShieldState() == SS_DOWN && !m_bInReload )
+ {
+ if ( (pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ if ( m_iClip1 > 0 )
+ {
+ // Fire the plasma shot
+ PrimaryAttack();
+ }
+ else
+ {
+ Reload();
+ }
+ }
+
+ // Reload button (or fire button when we're out of ammo)
+ if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ if ( pOwner->m_nButtons & IN_RELOAD )
+ {
+ Reload();
+ }
+ else if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ if ( !m_iClip1 && HasPrimaryAmmo() )
+ {
+ Reload();
+ }
+ }
+ }
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're charging
+ AllowShieldPostFrame( m_flNextPrimaryAttack <= gpGlobals->curtime || m_bInReload );
+
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShotgun::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ WeaponSound(SINGLE);
+
+ // Fire the bullets
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ float flDamage = weapon_combat_shotgun_damage.GetFloat();
+
+ m_bPoweredShot = false;
+ // Always allow a powered shot, even if they have less than the actual cost.
+ // Prevents them having to examine the energy bar carefully to know if they have enough.
+ if ( m_flOwnersEnergyLevel || pPlayer->HasPowerup(POWERUP_BOOST) )
+ {
+ if ( m_flOwnersEnergyLevel )
+ {
+ CPlayerClassSapper *pSapper = static_cast<CPlayerClassSapper*>( pPlayer->GetPlayerClass() );
+ pSapper->DeductDrainedEnergy( weapon_combat_shotgun_energy_cost.GetFloat() );
+ }
+
+ m_bPoweredShot = true;
+
+ flDamage = weapon_combat_shotgun_powered_damage.GetFloat();
+ }
+
+ // Make a satisfying shotgun force, and knock them into the air
+ float flForceScale = (100) * 75;
+ Vector vecForce = vecAiming;
+ vecForce.z += 0.7;
+ vecForce *= flForceScale;
+
+ CTakeDamageInfo info( this, pPlayer, vecForce, vec3_origin, flDamage, DMG_BULLET | DMG_BUCKSHOT);
+ TFGameRules()->FireBullets( info, weapon_combat_shotgun_pellets.GetFloat(), vecSrc, vecAiming, GetBulletSpread(), weapon_combat_shotgun_range.GetFloat(), m_iPrimaryAmmoType, 2, entindex(), 0 );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_iClip1 = m_iClip1 - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponCombatShotgun::GetFireRate( void )
+{
+ float flFireRate = ( SequenceDuration() * 2) + SHARED_RANDOMFLOAT( 0.0, 0.035f );
+
+ CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( GetOwner() );
+ if ( pPlayer )
+ {
+ // Ducking players should fire more rapidly.
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ flFireRate *= weapon_combat_shotgun_ducking_mod.GetFloat();
+ }
+ }
+
+ return flFireRate;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Match the anim speed to the weapon speed while crouching
+//-----------------------------------------------------------------------------
+float CWeaponCombatShotgun::GetDefaultAnimSpeed( void )
+{
+ if ( GetOwner() && GetOwner()->IsPlayer() )
+ {
+ if ( GetOwner()->GetFlags() & FL_DUCKING )
+ return (1.0 + (1.0 - weapon_combat_shotgun_ducking_mod.GetFloat()) );
+ }
+
+ return 1.0;
+}
+
+#if defined ( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponCombatShotgun::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return true;
+
+ if ( m_flOwnersEnergyLevel || pPlayer->HasPowerup(POWERUP_BOOST) )
+ {
+ Vector vecMuzzlePos, vecBarrelPos;
+ QAngle angMuzzle;
+ int iAttachment = pViewModel->LookupAttachment( "0" );
+ pViewModel->GetAttachment( iAttachment, vecBarrelPos, angMuzzle );
+ //pViewModel->UncorrectViewModelAttachment( vecBarrelPos );
+ iAttachment = pViewModel->LookupAttachment( "muzzle" );
+ pViewModel->GetAttachment( 0, vecMuzzlePos, angMuzzle );
+
+ unsigned char color[3];
+ color[0] = 50;
+ color[1] = 255;
+ color[2] = 50;
+ FX_MuzzleEffect( vecBarrelPos, angMuzzle, 1.0, GetRefEHandle(), &color[0] );
+
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShotgun::ViewModelDrawn( C_BaseViewModel *pViewModel )
+{
+ CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( m_flOwnersEnergyLevel )
+ {
+ Vector vecMuzzlePos, vecBarrelPos;
+ QAngle angMuzzle;
+ int iAttachment = pViewModel->LookupAttachment( "0" );
+ pViewModel->GetAttachment( iAttachment, vecBarrelPos, angMuzzle );
+ //pViewModel->UncorrectViewModelAttachment( vecBarrelPos );
+ iAttachment = pViewModel->LookupAttachment( "muzzle" );
+ pViewModel->GetAttachment( 0, vecMuzzlePos, angMuzzle );
+
+ unsigned char color[3];
+ color[0] = 50;
+ color[1] = 128;
+ color[2] = 50;
+ FX_Smoke( vecBarrelPos, angMuzzle, MAX(0.3,m_flOwnersEnergyLevel), 1, &color[0], 255 );
+ }
+}
+#endif \ No newline at end of file
diff --git a/game/shared/tf2/weapon_combat_usedwithshieldbase.cpp b/game/shared/tf2/weapon_combat_usedwithshieldbase.cpp
new file mode 100644
index 0000000..53fc9a2
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_usedwithshieldbase.cpp
@@ -0,0 +1,111 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "weapon_combatshield.h"
+#include "weapon_twohandedcontainer.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Make sure we're not switching directly to this weapon, since this
+// weapon can only be "switched" to by the twohandedcontainer weapon.
+//-----------------------------------------------------------------------------
+bool CWeaponCombatUsedWithShieldBase::CanDeploy( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if (!pPlayer)
+ return false;
+
+ // Make sure the current weapon is a twohanded weapon container
+ CWeaponTwoHandedContainer *pContainer = dynamic_cast<CWeaponTwoHandedContainer*>(pPlayer->GetActiveWeapon());
+ if ( !pContainer )
+ {
+ // Not the two handed container, so deploy the two handed container instead
+ pContainer = ( CWeaponTwoHandedContainer * )pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
+ if ( !pContainer )
+ return BaseClass::Deploy();
+
+ // Deploy the container
+ pPlayer->Weapon_Switch( pContainer );
+
+ // Make sure the container's using the desired weapon
+ pContainer->SetWeapons( this, pContainer->GetRightWeapon() );
+ return false;
+ }
+
+ return BaseClass::CanDeploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : allow -
+//-----------------------------------------------------------------------------
+void CWeaponCombatUsedWithShieldBase::AllowShieldPostFrame( bool allow )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ CWeaponCombatShield *shield = static_cast< CWeaponCombatShield * >( pOwner->Weapon_OwnsThisType( "weapon_combat_shield" ) );
+ if ( !shield )
+ return;
+
+ shield->SetAllowPostFrame( allow );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int CWeaponCombatUsedWithShieldBase::GetShieldState( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return SS_DOWN;
+
+ CWeaponCombatShield *pShield = pOwner->GetCombatShield();
+ if ( !pShield )
+ return SS_DOWN;
+
+ return pShield->GetShieldState();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Mirror the values in the container, if there is one
+// Input : *pPlayer -
+// Output : int
+//-----------------------------------------------------------------------------
+int CWeaponCombatUsedWithShieldBase::UpdateClientData( CBasePlayer *pPlayer )
+{
+ if ( !pPlayer )
+ {
+ return BaseClass::UpdateClientData( pPlayer );
+ }
+
+ CWeaponTwoHandedContainer *pContainer = ( CWeaponTwoHandedContainer * )pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
+ if ( !pContainer || pContainer != pPlayer->GetActiveWeapon() )
+ return BaseClass::UpdateClientData( pPlayer );
+
+ // Make sure this weapon is one of the container's active weapons
+ if ( pContainer->GetLeftWeapon() != this && pContainer->GetRightWeapon() != this )
+ return BaseClass::UpdateClientData( pPlayer );
+
+ int retval = pContainer->UpdateClientData( pPlayer );
+ m_iState = pContainer->m_iState;
+ return retval;
+}
+
+LINK_ENTITY_TO_CLASS( weapon_combat_usedwithshieldbase, CWeaponCombatUsedWithShieldBase );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatUsedWithShieldBase, DT_WeaponCombatUsedWithShieldBase )
+
+BEGIN_NETWORK_TABLE( CWeaponCombatUsedWithShieldBase, DT_WeaponCombatUsedWithShieldBase )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatUsedWithShieldBase )
+END_PREDICTION_DATA()
diff --git a/game/shared/tf2/weapon_combat_usedwithshieldbase.h b/game/shared/tf2/weapon_combat_usedwithshieldbase.h
new file mode 100644
index 0000000..7db6ea4
--- /dev/null
+++ b/game/shared/tf2/weapon_combat_usedwithshieldbase.h
@@ -0,0 +1,59 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_COMBAT_USEDWITHSHIELDBASE_H
+#define WEAPON_COMBAT_USEDWITHSHIELDBASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetfcombatweapon_shared.h"
+
+class CBasePlayer;
+
+#if defined( CLIENT_DLL )
+#define CWeaponCombatUsedWithShieldBase C_WeaponCombatUsedWithShieldBase
+#endif
+
+class CWeaponCombatUsedWithShieldBase : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponCombatUsedWithShieldBase, CBaseTFCombatWeapon );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatUsedWithShieldBase( void ) {}
+
+ virtual bool CanDeploy( void );
+ virtual int UpdateClientData( CBasePlayer *pPlayer );
+ virtual bool SupportsTwoHanded( void ) { return true; };
+ void AllowShieldPostFrame( bool allow );
+ virtual int GetShieldState( void );
+
+ /*
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+ */
+
+private:
+ CWeaponCombatUsedWithShieldBase( const CWeaponCombatUsedWithShieldBase & );
+
+};
+#endif // WEAPON_COMBAT_USEDWITHSHIELDBASE_H
diff --git a/game/shared/tf2/weapon_combatshield.cpp b/game/shared/tf2/weapon_combatshield.cpp
new file mode 100644
index 0000000..6b9d87d
--- /dev/null
+++ b/game/shared/tf2/weapon_combatshield.cpp
@@ -0,0 +1,1205 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Combative shield weapon
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_twohandedcontainer.h"
+#include "weapon_combatshield.h"
+#include "engine/IEngineSound.h"
+#include "in_buttons.h"
+#include "weapon_combat_usedwithshieldbase.h"
+
+#if !defined( CLIENT_DLL )
+#include "tf_shield.h"
+
+extern ConVar tf_knockdowntime;
+
+#else
+
+#include "iviewrender_beams.h"
+#include "c_team.h"
+#include "cdll_int.h"
+#include "hudelement.h"
+#include "bone_setup.h"
+#include "beamdraw.h"
+#include <vgui/ISurface.h>
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Damage CVars
+ConVar weapon_combat_shield_rechargetime( "weapon_combat_shield_rechargetime","4", FCVAR_REPLICATED, "Time after taking damage before the shields starts recharging" );
+ConVar weapon_combat_shield_rechargeamount( "weapon_combat_shield_rechargeamount","0.03", FCVAR_REPLICATED, "Amount shield recharges every 10th of a second (must be an int)" );
+ConVar weapon_combat_shield_factor( "weapon_combat_shield_factor","0.4", FCVAR_REPLICATED, "Factor applied to damage the shield blocks" );
+
+ConVar weapon_combat_shield_teslaspeed( "weapon_combat_shield_teslaspeed", "0.025f", FCVAR_REPLICATED, "Speed of the tesla effect on the view model." );
+ConVar weapon_combat_shield_teslaskitter( "weapon_combat_shield_teslaskitter", "0.3f", FCVAR_REPLICATED, "Speed of the tesla skitter effect (percentage)." );
+ConVar weapon_combat_shield_teslaeffect( "weapon_combat_shield_teslaeffect", "4", FCVAR_REPLICATED, "Experimenting with effects." );
+
+ConVar weapon_combat_shield_health( "weapon_combat_shield_health", "100", FCVAR_REPLICATED, "Combat shield's maximum health." );
+
+// HACK: If we don't set this then we get a pop when transitioning into / out of idle animation
+// for commando_test model because the origin is wrong
+// This can be removed once the model itself is fixed
+#define SHIELD_FADOUT_TIME 0.2f
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor:
+//-----------------------------------------------------------------------------
+CWeaponCombatShield::CWeaponCombatShield()
+{
+ m_bAllowPostFrame = true;
+ m_bHasShieldParry = false;
+ m_flShieldHealth = 1.0;
+#if defined( CLIENT_DLL )
+ m_flFlashTimeEnd = 0;
+
+ m_flTeslaSpeed = weapon_combat_shield_teslaspeed.GetFloat();
+ m_flTeslaSkitter = weapon_combat_shield_teslaskitter.GetFloat();
+ m_flTeslaLeftInc = 0.0f;
+ m_flTeslaRightInc = 0.0f;
+ m_pTeslaBeam = NULL;
+ m_pTeslaBeam2 = NULL;
+
+ m_flShieldInc = 1.0f;
+ m_pShieldBeam = NULL;
+ m_pShieldBeam2 = NULL;
+ m_pShieldBeam3 = NULL;
+#endif
+ SetPredictionEligible( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheModel( "sprites/blueflare1.vmt" );
+ PrecacheModel( "sprites/physbeam.vmt" );
+
+ PrecacheScriptSound( "WeaponCombatShield.TakeBash" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponCombatShield::Deploy( void )
+{
+ if ( BaseClass::Deploy() )
+ {
+ GainedNewTechnology(NULL);
+ SetShieldState( SS_DOWN );
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the activity the other weapon in our twohanded container should play for this activity
+//-----------------------------------------------------------------------------
+int CWeaponCombatShield::GetOtherWeaponsActivity( int iActivity )
+{
+ switch ( iActivity )
+ {
+ case ACT_VM_HAULBACK:
+ return ACT_VM_DRAW;
+
+ case ACT_VM_SECONDARYATTACK:
+ return ACT_VM_HOLSTER;
+
+ default:
+ break;
+ };
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the activity the other weapon in our twohanded container should
+// play instead of the one it's attempting to play.
+//-----------------------------------------------------------------------------
+int CWeaponCombatShield::ReplaceOtherWeaponsActivity( int iActivity )
+{
+ switch ( iActivity )
+ {
+ case ACT_VM_IDLE:
+ // If I'm active, don't let it idle
+ if ( GetShieldState() != SS_DOWN )
+ return -1;
+
+ default:
+ break;
+ };
+
+ return BaseClass::ReplaceOtherWeaponsActivity(iActivity);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponCombatShield::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ CBaseTFPlayer *player = ToBaseTFPlayer( GetOwner() );
+ if ( player )
+ {
+ player->SetBlocking( false );
+ player->SetParrying( false );
+
+ if ( m_iShieldState != SS_DOWN &&
+ m_iShieldState != SS_UNAVAILABLE )
+ {
+ SetShieldState( SS_LOWERING );
+ }
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: I've been bashed by another player's shield
+//-----------------------------------------------------------------------------
+bool CWeaponCombatShield::TakeShieldBash( CBaseTFPlayer *pBasher )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return false;
+
+ // If I'm blocking, drop my block and prevent me from doing anything
+ if ( GetShieldState() == SS_UP ||
+ GetShieldState() == SS_RAISING ||
+ GetShieldState() == SS_LOWERING )
+ {
+ // Make the shield unavailable
+ SetShieldState( SS_UNAVAILABLE );
+ SendWeaponAnim( ACT_VM_HITCENTER );
+
+ m_flShieldUnavailableEndTime = gpGlobals->curtime + 2.0;
+
+ // Play a sound
+ EmitSound( "WeaponCombatShield.TakeBash" );
+ return true;
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::SetShieldState( int iShieldState )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ switch (iShieldState )
+ {
+ default:
+ case SS_DOWN:
+ pOwner->SetBlocking( false );
+ m_flShieldUpStartTime = 0;
+ m_flShieldParryEndTime = 0;
+ m_flShieldUnavailableEndTime = 0;
+ m_flShieldRaisedTime = 0.0f;
+ m_flShieldLoweredTime = 0.0f;
+ break;
+
+ case SS_UP:
+ SendWeaponAnim( ACT_VM_FIDGET );
+ pOwner->SetBlocking( true );
+ m_flShieldDownStartTime = 0.0f;
+ m_flShieldParryEndTime = 0;
+ m_flShieldUnavailableEndTime = 0;
+ m_flShieldRaisedTime = 0.0f;
+ m_flShieldLoweredTime = 0.0f;
+ break;
+
+ case SS_PARRYING:
+ pOwner->SetBlocking( false );
+ pOwner->SetParrying( true );
+ m_flShieldParryEndTime = gpGlobals->curtime + PARRY_OPPORTUNITY_LENGTH;
+ m_flShieldParrySwingEndTime = gpGlobals->curtime + 1.0f; // a hack to make it look ok
+ m_flShieldUnavailableEndTime = gpGlobals->curtime + SequenceDuration();
+ m_flNextPrimaryAttack = m_flShieldUnavailableEndTime;
+ m_flShieldRaisedTime = 0.0f;
+ m_flShieldLoweredTime = 0.0f;
+ break;
+
+ case SS_PARRYING_FINISH_SWING:
+ pOwner->SetBlocking( false );
+ pOwner->SetParrying( false );
+ break;
+
+ case SS_UNAVAILABLE:
+ SendWeaponAnim( ACT_VM_HAULBACK );
+ pOwner->SetBlocking( false );
+ pOwner->SetParrying( false );
+ break;
+
+ case SS_RAISING:
+ {
+ pOwner->SetBlocking( false );
+ pOwner->SetParrying( false );
+ m_flShieldRaisedTime = gpGlobals->curtime + SequenceDuration();
+ m_flShieldUpStartTime = gpGlobals->curtime;
+ }
+ break;
+ case SS_LOWERING:
+ {
+ SendWeaponAnim( ACT_VM_HAULBACK );
+ pOwner->SetBlocking( false );
+ pOwner->SetParrying( false );
+ m_flShieldLoweredTime = gpGlobals->curtime + SequenceDuration();
+ m_flShieldDownStartTime = gpGlobals->curtime;
+ }
+ break;
+ };
+
+ m_iShieldState = iShieldState;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check to see if the shield state should change
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::UpdateShieldState( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Check to see if I should move out of the current state
+ switch ( m_iShieldState )
+ {
+ default:
+ case SS_DOWN:
+ case SS_UP:
+ break;
+
+ case SS_RAISING:
+ if ( gpGlobals->curtime > m_flShieldRaisedTime )
+ {
+ SetShieldState( SS_UP );
+ }
+ break;
+ case SS_LOWERING:
+ if ( gpGlobals->curtime > m_flShieldLoweredTime )
+ {
+ SetShieldState( SS_DOWN );
+ }
+ break;
+
+ case SS_PARRYING:
+ if ( gpGlobals->curtime > m_flShieldParryEndTime )
+ {
+ SetShieldState( SS_PARRYING_FINISH_SWING );
+ }
+ break;
+
+ case SS_PARRYING_FINISH_SWING:
+ if ( gpGlobals->curtime > m_flShieldParrySwingEndTime )
+ {
+ SetShieldState( SS_UNAVAILABLE );
+ }
+ break;
+
+ case SS_UNAVAILABLE:
+ if ( gpGlobals->curtime > m_flShieldUnavailableEndTime )
+ {
+ SetShieldState( SS_DOWN );
+ }
+ break;
+ };
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CWeaponCombatShield::GetShieldState( void )
+{
+ return m_iShieldState;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::SetShieldUsable( bool bUsable )
+{
+ // Shutting down!
+ if ( !bUsable )
+ {
+ if ( m_iShieldState == SS_UP || m_iShieldState == SS_RAISING )
+ {
+ // We've got our shield up, so drop it (play sound & animation).
+ SendWeaponAnim( ACT_VM_HAULBACK );
+ WeaponSound( SPECIAL2 );
+ SetShieldState( SS_LOWERING );
+ }
+ }
+
+ m_bUsable = bUsable;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponCombatShield::ShieldUsable( void )
+{
+ return m_bUsable;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : allow -
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::SetAllowPostFrame( bool allow )
+{
+ m_bAllowPostFrame = allow;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::ItemPostFrame( void )
+{
+ if ( m_bAllowPostFrame )
+ {
+ ShieldPostFrame();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allow the shield to interrupt reloads, etc.
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::ItemBusyFrame( void )
+{
+ if ( m_bAllowPostFrame )
+ {
+ ShieldPostFrame();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The player holding this weapon has just gained new technology.
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+ BaseClass::GainedNewTechnology( pTechnology );
+
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // Has a parry?
+ if ( pPlayer->HasNamedTechnology( "com_comboshield_parry" ) )
+ {
+ m_bHasShieldParry = true;
+ }
+ else
+ {
+ m_bHasShieldParry = false;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle the shield input
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::ShieldPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ UpdateShieldState();
+ CheckReload();
+
+ // Store off EMP state
+ bool isEMPed = IsOwnerEMPed();
+
+ // If the shield's unavailable, just abort
+ if ( GetShieldState() == SS_UNAVAILABLE )
+ return;
+
+ if ( m_flNextPrimaryAttack > gpGlobals->curtime )
+ return;
+
+ bool shieldRaised = ( GetShieldState() == SS_UP );
+ bool shieldRaising = ( GetShieldState() == SS_RAISING );
+
+ // GetShieldState() == SS_LOWERING );
+
+ // If my shield's out of power, I can't do anything with it
+ if ( !GetShieldHealth() )
+ return;
+
+ // Was the shield button just pressed?
+ if ( GetShieldState() == SS_DOWN && !isEMPed && pOwner->m_nButtons & IN_ATTACK2 )
+ {
+ // Play sound & anim
+ WeaponSound( SPECIAL1 );
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+ SetShieldState( SS_RAISING );
+
+ // Abort any reloads in progess
+ pOwner->AbortReload();
+ }
+ else if ( ( shieldRaised || shieldRaising ) && !FBitSet( pOwner->m_nButtons, IN_ATTACK2 ) )
+ {
+ // Shield button was just released, check to see if we were parrying
+ bool shouldParry = (gpGlobals->curtime < (m_flShieldUpStartTime + PARRY_DETECTION_TIME ));
+
+ if ( m_bHasShieldParry && shouldParry )
+ {
+ // Parry!
+ // Play sound & anim
+ WeaponSound( SPECIAL2 );
+ SendWeaponAnim( ACT_VM_SWINGHIT );
+
+ SetShieldState( SS_PARRYING );
+
+ // Bash enemies in front of me
+ ShieldBash();
+ }
+ else
+ {
+ // Player's just lowered his shield
+ // Play sound & anim
+ WeaponSound( SPECIAL2 );
+ SendWeaponAnim( ACT_VM_HAULBACK );
+
+ SetShieldState( SS_LOWERING );
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ }
+ }
+ else if ( GetShieldState() == SS_UP && ( pOwner->m_nButtons & IN_ATTACK2 ) && ( isEMPed ) )
+ {
+ // We've got our shield up, and we were just EMPed, so drop it
+ // Play sound & anim
+ SendWeaponAnim( ACT_VM_HAULBACK );
+ WeaponSound( SPECIAL2 );
+
+ SetShieldState( SS_LOWERING );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Bash enemies in front of me with my shield
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::ShieldBash( void )
+{
+#if 0
+ // ROBIN: Disabled shield bash
+ return;
+
+ // Get any players in front of me
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Get the target point and location
+ Vector vecAiming;
+ Vector vecSrc = pOwner->Weapon_ShootPosition( pOwner->GetOrigin() );
+ pOwner->EyeVectors( &vecAiming );
+
+ // Find a player in range of this player, and make sure they're healable
+ trace_t tr;
+ Vector vecEnd = vecSrc + (vecAiming * SHIELD_BASH_RANGE);
+ UTIL_TraceLine( vecSrc, vecEnd, MASK_SHOT, pOwner->edict(), COLLISION_GROUP_NONE, &tr);
+ if (tr.fraction != 1.0)
+ {
+ CBaseEntity *pEntity = CBaseEntity::Instance(tr.u.ent);
+ if ( pEntity )
+ {
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( pEntity );
+ if ( pPlayer && (pPlayer != pOwner) )
+ {
+ // Target needs to be on the eneny team
+ if ( pPlayer->IsAlive() && !pPlayer->InSameTeam( pOwner ) )
+ {
+ // Ok, we have an enemy player
+ pPlayer->TakeShieldBash( pOwner );
+ }
+ }
+ }
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Attempt to block the incoming attack, and return the damage it
+// should do after the block, if any.
+//-----------------------------------------------------------------------------
+float CWeaponCombatShield::AttemptToBlock( float flDamage )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer || !weapon_combat_shield_factor.GetFloat() )
+ return 0;
+
+ // Block as much of the damage as we can
+ float flPowerNeeded = flDamage * weapon_combat_shield_factor.GetFloat();
+ flPowerNeeded = RemapVal( flPowerNeeded, 0, weapon_combat_shield_health.GetFloat(), 0, 1 );
+ float flPowerUsed = MIN( flPowerNeeded, GetShieldHealth() );
+
+#ifndef CLIENT_DLL
+ RemoveShieldHealth( flPowerUsed );
+
+ // Start recharging shortly after taking damage
+ SetThink( ShieldRechargeThink );
+ SetNextThink( gpGlobals->curtime + weapon_combat_shield_rechargetime.GetFloat() );
+#endif
+
+ // Failed to block it all?
+ if ( flPowerUsed < flPowerNeeded )
+ {
+#ifndef CLIENT_DLL
+ // Force the shield to drop if it's up
+ if ( GetShieldState() == SS_UP )
+ {
+ // Play sound & anim
+ SendWeaponAnim( ACT_VM_HAULBACK );
+ WeaponSound( SPECIAL2 );
+ SetShieldState( SS_LOWERING );
+ }
+#endif
+
+ return ( flDamage - (flPowerUsed * (1.0 / weapon_combat_shield_factor.GetFloat())) );
+ }
+
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponCombatShield::GetShieldHealth( void )
+{
+ return m_flShieldHealth;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::AddShieldHealth( float flHealth )
+{
+ m_flShieldHealth = MIN( 1.0, m_flShieldHealth + flHealth );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::RemoveShieldHealth( float flHealth )
+{
+ m_flShieldHealth = MAX( 0.0, m_flShieldHealth - flHealth );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recharge the shield
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::ShieldRechargeThink( void )
+{
+// FIXME:
+//xxx
+#if !defined( CLIENT_DLL )
+ if ( GetShieldHealth() >= 1.0 )
+ {
+ SetThink( NULL );
+ return;
+ }
+
+ AddShieldHealth( weapon_combat_shield_rechargeamount.GetFloat() );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+#endif
+}
+
+
+//====================================================================================
+// WEAPON CLIENT HANDLING
+//====================================================================================
+int CWeaponCombatShield::UpdateClientData( CBasePlayer *pPlayer )
+{
+ if ( !pPlayer )
+ {
+ return BaseClass::UpdateClientData( pPlayer );
+ }
+
+ CWeaponTwoHandedContainer *pContainer = ( CWeaponTwoHandedContainer * )pPlayer->Weapon_OwnsThisType( "weapon_twohandedcontainer" );
+ if ( !pContainer || pContainer != pPlayer->GetActiveWeapon() )
+ return BaseClass::UpdateClientData( pPlayer );
+
+ // Make sure this weapon is one of the container's active weapons
+ if ( pContainer->GetLeftWeapon() != this && pContainer->GetRightWeapon() != this )
+ return BaseClass::UpdateClientData( pPlayer );
+
+ int retval = pContainer->UpdateClientData( pPlayer );
+ m_iState = pContainer->m_iState;
+ return retval;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponCombatShield::VisibleInWeaponSelection( void )
+{
+ return false;
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+bool CWeaponCombatShield::IsUp( void )
+{
+ return ( GetShieldState() == SS_UP );
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+float CWeaponCombatShield::GetRaisingTime( void )
+{
+ if ((GetShieldState() != SS_UP ) && (GetShieldState() != SS_RAISING))
+ return 0.0f;
+
+ return gpGlobals->curtime - m_flShieldUpStartTime;
+}
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+float CWeaponCombatShield::GetLoweringTime( void )
+{
+ if ((GetShieldState() != SS_DOWN ) && (GetShieldState() != SS_LOWERING))
+ return 0.0f;
+
+ return gpGlobals->curtime - m_flShieldDownStartTime;
+}
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose: Draw the ammo counts
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::DrawAmmo( void )
+{
+ // ROBIN: Removed this now that the shield colors itself to show health level
+ return;
+
+ int r, g, b, a;
+ int x, y;
+
+ // Get the shield power level
+ float flPowerLevel = GetShieldHealth();
+ float flInverseFactor = 1.0 - flPowerLevel;
+
+ // Set our color
+ gHUD.m_clrNormal.GetColor( r, g, b, a );
+
+ int iWidth = XRES(12);
+ int iHeight = YRES(64);
+
+ x = XRES(548);
+ y = ( ScreenHeight() - YRES(2) - iHeight );
+
+ // Flashing the power level?
+ float flFlash = 0;
+ if ( gpGlobals->curtime < m_flFlashTimeEnd && !GetPrimaryAmmo() )
+ {
+ flFlash = fmod( gpGlobals->curtime, 0.25 );
+ flFlash *= 2 * M_PI;
+ flFlash = cos( flFlash );
+ }
+
+ // draw the exhausted portion of the bar.
+ vgui::surface()->DrawSetColor( Color( r, g * flPowerLevel, b * flPowerLevel, 100 + (flFlash * 100) ) );
+ vgui::surface()->DrawFilledRect( x, y, x + iWidth, y + iHeight * flInverseFactor );
+
+ // draw the powerered portion of the bar
+ vgui::surface()->DrawSetColor( Color( r, g * flPowerLevel, b * flPowerLevel, 190 ) );
+ vgui::surface()->DrawFilledRect( x, y + iHeight * flInverseFactor, x + iWidth, y + iHeight * flInverseFactor + iHeight * flPowerLevel);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::GetViewmodelBoneControllers( C_BaseViewModel *pViewModel, float controllers[MAXSTUDIOBONECTRLS])
+{
+ // Dial shows the shield power level
+ float flPowerLevel = 1.0;
+ if ( GetShieldState() == SS_UP )
+ {
+ flPowerLevel = GetShieldHealth();
+ }
+ else if ( GetShieldState() == SS_RAISING )
+ {
+ // Bring the power up with the animation
+ float flTotal = m_flShieldRaisedTime - m_flShieldUpStartTime;
+ float flCurrent = (gpGlobals->curtime - m_flShieldUpStartTime);
+ flPowerLevel = flCurrent / flTotal;
+ }
+ else if ( GetShieldState() == SS_LOWERING )
+ {
+ // Bring the power down with the animation
+ float flTotal = (m_flShieldLoweredTime - m_flShieldDownStartTime);
+ float flCurrent = (gpGlobals->curtime - m_flShieldDownStartTime);
+ flPowerLevel = 1.0 - (flCurrent / flTotal);
+ }
+
+ // Make the middle point be full power (right of the middle being powered up)
+ // Adjust a little for the perspective.
+ flPowerLevel *= 0.55;
+
+ // Add some shake
+ flPowerLevel += RandomFloat( -0.02, 0.02 );
+ controllers[0] = flPowerLevel;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::ViewModelDrawn( C_BaseViewModel *pViewModel )
+{
+ if ( m_iShieldState == SS_DOWN )
+ return;
+
+ DrawBeams( pViewModel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::InitShieldBeam( void )
+{
+ BeamInfo_t beamInfo;
+
+ beamInfo.m_vecStart.Init();
+ beamInfo.m_vecEnd.Init();
+ beamInfo.m_pszModelName = "sprites/physbeam.vmt";
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+ beamInfo.m_flWidth = 2.0f;
+ beamInfo.m_flEndWidth = 2.0f;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 4.0f;
+ beamInfo.m_flBrightness = 50.0f;
+ beamInfo.m_flSpeed = 5.0f;
+ beamInfo.m_nStartFrame = 0;
+ beamInfo.m_flFrameRate = 0.0f;
+ beamInfo.m_flRed = 255.0f;
+ beamInfo.m_flGreen = 255.0f;
+ beamInfo.m_flBlue = 128.0f;
+ beamInfo.m_nSegments = 15;
+ beamInfo.m_bRenderable = false;
+
+ m_pShieldBeam = beams->CreateBeamPoints( beamInfo );
+
+ beamInfo.m_vecStart.Init();
+ beamInfo.m_vecEnd.Init();
+ beamInfo.m_pszModelName = "sprites/physbeam.vmt";
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+ beamInfo.m_flWidth = 1.5f;
+ beamInfo.m_flEndWidth = 1.5f;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 8.0f;
+ beamInfo.m_flBrightness = 75.0f;
+ beamInfo.m_flSpeed = 10.0f;
+ beamInfo.m_nStartFrame = 0;
+ beamInfo.m_flFrameRate = 0.0f;
+ beamInfo.m_flRed = 255.0f;
+ beamInfo.m_flGreen = 255.0f;
+ beamInfo.m_flBlue = 128.0f;
+ beamInfo.m_nSegments = 20;
+ beamInfo.m_bRenderable = false;
+
+ m_pShieldBeam2 = beams->CreateBeamPoints( beamInfo );
+
+ beamInfo.m_vecStart.Init();
+ beamInfo.m_vecEnd.Init();
+ beamInfo.m_pszModelName = "sprites/physbeam.vmt";
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+ beamInfo.m_flWidth = 3.0f;
+ beamInfo.m_flEndWidth = 3.0f;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 5.5f;
+ beamInfo.m_flBrightness = 50.0f;
+ beamInfo.m_flSpeed = 10.0f;
+ beamInfo.m_nStartFrame = 0;
+ beamInfo.m_flFrameRate = 0.0f;
+ beamInfo.m_flRed = 255.0f;
+ beamInfo.m_flGreen = 255.0f;
+ beamInfo.m_flBlue = 128.0f;
+ beamInfo.m_nSegments = 18;
+ beamInfo.m_bRenderable = false;
+
+ m_pShieldBeam3 = beams->CreateBeamPoints( beamInfo );
+
+ m_hShieldSpriteMaterial.Init( "sprites/blueflare1", TEXTURE_GROUP_CLIENT_EFFECTS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::InitTeslaBeam( void )
+{
+ BeamInfo_t beamInfo;
+
+ beamInfo.m_vecStart.Init();
+ beamInfo.m_vecEnd.Init();
+ beamInfo.m_pszModelName = "sprites/blueflare1.vmt";
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+ beamInfo.m_flWidth = 1.0f;
+ beamInfo.m_flEndWidth = 1.0f;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 16.0f;
+ beamInfo.m_flBrightness = 255.0f;
+ beamInfo.m_flSpeed = 25.0f;
+ beamInfo.m_nStartFrame = 0;
+ beamInfo.m_flFrameRate = 0.0f;
+ beamInfo.m_flRed = 206.0f;
+ beamInfo.m_flGreen = 181.0f;
+ beamInfo.m_flBlue = 127.0f;
+ beamInfo.m_nSegments = 15;
+ beamInfo.m_bRenderable = false;
+
+ m_pTeslaBeam = beams->CreateBeamPoints( beamInfo );
+
+ beamInfo.m_vecStart.Init();
+ beamInfo.m_vecEnd.Init();
+ beamInfo.m_pszModelName = "sprites/blueflare1.vmt";
+ beamInfo.m_flHaloScale = 0.0f;
+ beamInfo.m_flLife = 0.0f;
+ beamInfo.m_flWidth = 1.0f;
+ beamInfo.m_flEndWidth = 1.0f;
+ beamInfo.m_flFadeLength = 0.0f;
+ beamInfo.m_flAmplitude = 27.0f;
+ beamInfo.m_flBrightness = 100.0f;
+ beamInfo.m_flSpeed = 15.0f;
+ beamInfo.m_nStartFrame = 0;
+ beamInfo.m_flFrameRate = 0.0f;
+ beamInfo.m_flRed = 206.0f;
+ beamInfo.m_flGreen = 181.0f;
+ beamInfo.m_flBlue = 127.0f;
+ beamInfo.m_nSegments = 8;
+ beamInfo.m_bRenderable = false;
+
+ m_pTeslaBeam2 = beams->CreateBeamPoints( beamInfo );
+
+ m_hTeslaSpriteMaterial.Init( "sprites/blueflare1", TEXTURE_GROUP_CLIENT_EFFECTS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Render a tesla beam in the veiw model.
+//
+// NOTE: This is a big ugly mess that will get cleaned up when I nail down
+// the effect.
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::DrawBeams( C_BaseViewModel *pViewModel )
+{
+ // Verify data.
+ if ( !pViewModel )
+ return;
+
+ // Only humans have the tesla effects
+ if ( GetTeamNumber() == TEAM_ALIENS )
+ return;
+
+ // Init
+ if ( !m_pTeslaBeam )
+ {
+ InitTeslaBeam();
+ }
+ if ( !m_pShieldBeam )
+ {
+ InitShieldBeam();
+ }
+
+ if ( !m_pShieldBeam || !m_pTeslaBeam )
+ return;
+
+ // Variables
+ BeamInfo_t beamInfo;
+ QAngle vecAngle;
+ int iAttachment;
+
+ // Setup a color reflecting the health
+ float flShieldHealth = GetShieldHealth();
+ color32 color;
+ color.r = 206;
+ color.g = flShieldHealth * 182;
+ color.b = flShieldHealth * 127;
+ color.a = 255;
+
+ // Tesla Effect
+ Vector vecRightTop, vecRightBottom;
+ Vector vecLeftTop, vecLeftBottom;
+ iAttachment = pViewModel->LookupAttachment( "LeftBottom" );
+ pViewModel->GetAttachment( iAttachment, vecLeftBottom, vecAngle );
+ pViewModel->UncorrectViewModelAttachment( vecLeftBottom );
+
+ iAttachment = pViewModel->LookupAttachment( "LeftTip" );
+ pViewModel->GetAttachment( iAttachment, vecLeftTop, vecAngle );
+ pViewModel->UncorrectViewModelAttachment( vecLeftTop );
+
+ iAttachment = pViewModel->LookupAttachment( "RightBottom" );
+ pViewModel->GetAttachment( iAttachment, vecRightBottom, vecAngle );
+ pViewModel->UncorrectViewModelAttachment( vecRightBottom );
+
+ iAttachment = pViewModel->LookupAttachment( "RightTip" );
+ pViewModel->GetAttachment( iAttachment, vecRightTop, vecAngle );
+ pViewModel->UncorrectViewModelAttachment( vecRightTop );
+
+ m_flTeslaLeftInc += weapon_combat_shield_teslaspeed.GetFloat();
+ m_flTeslaRightInc += weapon_combat_shield_teslaspeed.GetFloat();
+ if ( m_flTeslaLeftInc > 1.0f ) { m_flTeslaLeftInc = 0.0f; }
+ if ( m_flTeslaRightInc > 1.0f ) { m_flTeslaRightInc = 0.0f; }
+
+ Vector vecLeft = vecLeftTop - vecLeftBottom;
+ Vector vecRight = vecRightTop - vecRightBottom;
+ Vector vecStart = vecLeftBottom + ( m_flTeslaLeftInc * vecLeft );
+ Vector vecEnd = vecRightBottom + ( m_flTeslaRightInc * vecRight );
+
+ beamInfo.m_vecStart = vecStart;
+ beamInfo.m_vecEnd = vecEnd;
+ beamInfo.m_flRed = color.r;
+ beamInfo.m_flGreen = color.g;
+ beamInfo.m_flBlue = color.b;
+ beams->UpdateBeamInfo( m_pTeslaBeam, beamInfo );
+ beams->UpdateBeamInfo( m_pTeslaBeam2, beamInfo );
+ beams->DrawBeam( m_pTeslaBeam );
+ beams->DrawBeam( m_pTeslaBeam2 );
+
+ // Draw a sprite at the tip of the tesla coil.
+ float flSize = 4.0f;
+
+ materials->Bind( m_hShieldSpriteMaterial, this );
+ DrawSprite( vecStart, flSize, flSize, color );
+ DrawSprite( vecEnd, flSize, flSize, color );
+
+ // Shield Effect
+ float flPercentage = random->RandomFloat( 0.0f, 1.0f );
+ if ( flPercentage < weapon_combat_shield_teslaskitter.GetFloat() )
+ {
+ char szShieldJoint[16];
+ int nJoint = random->RandomInt( 1, 8 );
+ Q_snprintf( szShieldJoint, sizeof( szShieldJoint ), "Shield%d", nJoint );
+
+ Vector vecJoint;
+ int iAttachment = pViewModel->LookupAttachment( &szShieldJoint[0] );
+ pViewModel->GetAttachment( iAttachment, vecJoint, vecAngle );
+ pViewModel->UncorrectViewModelAttachment( vecJoint );
+
+ if ( nJoint < 5 )
+ {
+ beamInfo.m_vecStart = vecLeftTop;
+ }
+ else
+ {
+ beamInfo.m_vecStart = vecRightTop;
+ }
+ beamInfo.m_vecEnd = vecJoint;
+ beams->UpdateBeamInfo( m_pTeslaBeam, beamInfo );
+ beams->UpdateBeamInfo( m_pTeslaBeam2, beamInfo );
+ beams->DrawBeam( m_pTeslaBeam );
+ beams->DrawBeam( m_pTeslaBeam2 );
+
+ float flSize = 7.0f;
+ color32 color = { 206, 181, 127, 255 };
+ materials->Bind( m_hShieldSpriteMaterial, this );
+ DrawSprite( beamInfo.m_vecStart, flSize, flSize, color );
+ }
+
+#if 0
+ // Shield Effect
+ char szShieldJoint[16];
+ Vector vecShieldJoints[8];
+ for( int iJoint = 0; iJoint < 8; ++iJoint )
+ {
+ Q_snprintf( szShieldJoint, sizeof( szShieldJoint ), "Shield%d", iJoint+1 );
+ iAttachment = pViewModel->LookupAttachment( &szShieldJoint[0] );
+ pViewModel->GetAttachment( iAttachment, vecShieldJoints[iJoint], vecAngle );
+ pViewModel->UncorrectViewModelAttachment( vecShieldJoints[iJoint] );
+ }
+
+ // Shield Internal
+ if ( m_flShieldInc < 1.0f )
+ {
+ Vector vecEdge, vecEnd;
+ if ( m_bLeftToRight )
+ {
+ vecEdge = vecShieldJoints[((m_nShieldEdge+1)%8)] - vecShieldJoints[m_nShieldEdge];
+ vecEnd = vecShieldJoints[m_nShieldEdge] + ( m_flShieldInc * vecEdge );
+ }
+ else
+ {
+ vecEdge = vecShieldJoints[m_nShieldEdge] - vecShieldJoints[((m_nShieldEdge+1)%8)];
+ vecEnd = vecShieldJoints[((m_nShieldEdge+1)%8)] + ( m_flShieldInc * vecEdge );
+ }
+
+ if ( m_nShieldEdge < 5 )
+ {
+ beamInfo.m_vecStart = vecLeftTop;
+ }
+ else
+ {
+ beamInfo.m_vecStart = vecRightTop;
+ }
+
+ beamInfo.m_vecEnd = vecEnd;
+ beams->UpdateBeamPoints( m_pShieldBeam2, beamInfo );
+ beams->UpdateBeamPoints( m_pShieldBeam3, beamInfo );
+ beams->DrawBeam( m_pShieldBeam2 );
+ beams->DrawBeam( m_pShieldBeam3 );
+
+ m_flShieldInc += m_flShieldSpeed;
+ }
+ else
+ {
+ m_flShieldInc = 0.0f;
+ m_flShieldSpeed = random->RandomFloat( 0.015f, 0.15f );
+ m_nShieldEdge = random->RandomInt( 0, 7 );
+
+ float flSide = random->RandomFloat( 0.0f, 1.0f );
+ m_bLeftToRight = ( flSide < 0.5f );
+ }
+
+ // Shield Outline
+ for( iJoint = 0; iJoint < 8; ++iJoint )
+ {
+ beamInfo.m_vecStart = vecShieldJoints[iJoint];
+ beamInfo.m_vecEnd = vecShieldJoints[(iJoint+1)%8];
+ beams->UpdateBeamPoints( m_pShieldBeam, beamInfo );
+ beams->UpdateBeamPoints( m_pShieldBeam2, beamInfo );
+ beams->DrawBeam( m_pShieldBeam );
+ beams->DrawBeam( m_pShieldBeam2 );
+
+ // Draw a sprite at the tip of the tesla coil.
+ float flSize = 3.0f;
+ color32 color = { 255, 255, 0, 255 };
+ materials->Bind( m_hShieldSpriteMaterial, this );
+ DrawSprite( vecShieldJoints[iJoint], flSize, flSize, color );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: If the shield has no health, and they're trying to raise it, flash the power level
+//-----------------------------------------------------------------------------
+void CWeaponCombatShield::HandleInput( void )
+{
+ // If the player's dead, ignore input
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( !pPlayer || pPlayer->GetHealth() < 0 )
+ return;
+
+ // Attempting to raise the shield?
+ if ( !GetShieldHealth() && ( gHUD.m_iKeyBits & IN_ATTACK2 ) )
+ {
+ m_flFlashTimeEnd = gpGlobals->curtime + 1.0;
+ }
+}
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_combat_shield, CWeaponCombatShield );
+LINK_ENTITY_TO_CLASS( weapon_combat_shield_alien, CWeaponCombatShieldAlien );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatShield , DT_WeaponCombatShield )
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCombatShieldAlien, DT_WeaponCombatShieldAlien )
+
+#if !defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose: Only send the LocalWeaponData to the player carrying the weapon
+//-----------------------------------------------------------------------------
+void* SendProxy_SendCombatShieldLocalWeaponDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
+{
+ // Get the weapon entity
+ CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData;
+ if ( pWeapon )
+ {
+ // Only send this chunk of data to the player carrying this weapon
+ CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() );
+ if ( pPlayer )
+ {
+ pRecipients->SetOnly( pPlayer->GetClientIndex() );
+ return (void*)pVarData;
+ }
+ }
+
+ return NULL;
+}
+REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendCombatShieldLocalWeaponDataTable );
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Propagation data for weapons. Only sent when a player's holding it.
+//-----------------------------------------------------------------------------
+BEGIN_NETWORK_TABLE_NOBASE( CWeaponCombatShield, DT_WeaponCombatShieldLocal )
+#if !defined( CLIENT_DLL )
+ SendPropTime( SENDINFO( m_flShieldParryEndTime ) ),
+ SendPropTime( SENDINFO( m_flShieldParrySwingEndTime ) ),
+ SendPropTime( SENDINFO( m_flShieldUnavailableEndTime ) ),
+ SendPropTime( SENDINFO( m_flShieldRaisedTime ) ),
+ SendPropTime( SENDINFO( m_flShieldLoweredTime ) ),
+#else
+ RecvPropTime( RECVINFO( m_flShieldParryEndTime ) ),
+ RecvPropTime( RECVINFO( m_flShieldParrySwingEndTime ) ),
+ RecvPropTime( RECVINFO( m_flShieldUnavailableEndTime ) ),
+ RecvPropTime( RECVINFO( m_flShieldRaisedTime ) ),
+ RecvPropTime( RECVINFO( m_flShieldLoweredTime ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_NETWORK_TABLE( CWeaponCombatShield , DT_WeaponCombatShield )
+#if !defined( CLIENT_DLL )
+ SendPropDataTable("this", 0, &REFERENCE_SEND_TABLE(DT_WeaponCombatShieldLocal), SendProxy_SendCombatShieldLocalWeaponDataTable ),
+ SendPropTime( SENDINFO( m_flShieldUpStartTime ) ),
+ SendPropTime( SENDINFO( m_flShieldDownStartTime ) ),
+ SendPropInt( SENDINFO( m_iShieldState ), 3, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_bAllowPostFrame ), 1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_bHasShieldParry ), 1, SPROP_UNSIGNED ),
+ SendPropFloat(SENDINFO( m_flShieldHealth ), 8, SPROP_ROUNDDOWN, 0.0f, 1.0f ),
+#else
+ RecvPropDataTable("this", 0, 0, &REFERENCE_RECV_TABLE(DT_WeaponCombatShieldLocal)),
+ RecvPropTime( RECVINFO( m_flShieldUpStartTime ) ),
+ RecvPropTime( RECVINFO( m_flShieldDownStartTime ) ),
+ RecvPropInt( RECVINFO( m_iShieldState ) ),
+ RecvPropInt( RECVINFO( m_bAllowPostFrame ) ),
+ RecvPropInt( RECVINFO( m_bHasShieldParry ) ),
+ RecvPropFloat(RECVINFO( m_flShieldHealth ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_NETWORK_TABLE( CWeaponCombatShieldAlien, DT_WeaponCombatShieldAlien )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatShield )
+
+ DEFINE_PRED_FIELD( m_iShieldState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bAllowPostFrame, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bHasShieldParry, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flShieldHealth, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_FIELD_TOL( m_flShieldUpStartTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+ DEFINE_PRED_FIELD_TOL( m_flShieldDownStartTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+ DEFINE_PRED_FIELD_TOL( m_flShieldParryEndTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+ DEFINE_PRED_FIELD_TOL( m_flShieldParrySwingEndTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+ DEFINE_PRED_FIELD_TOL( m_flShieldUnavailableEndTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+ DEFINE_PRED_FIELD_TOL( m_flShieldRaisedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+ DEFINE_PRED_FIELD_TOL( m_flShieldLoweredTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA( CWeaponCombatShieldAlien )
+END_PREDICTION_DATA()
+
+PRECACHE_WEAPON_REGISTER(weapon_combat_shield);
+PRECACHE_WEAPON_REGISTER(weapon_combat_shield_alien);
+
+
+
+
+
diff --git a/game/shared/tf2/weapon_combatshield.h b/game/shared/tf2/weapon_combatshield.h
new file mode 100644
index 0000000..fdece5c
--- /dev/null
+++ b/game/shared/tf2/weapon_combatshield.h
@@ -0,0 +1,185 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_COMBATSHIELD_SHARED_H
+#define WEAPON_COMBATSHIELD_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetfcombatweapon_shared.h"
+
+// Shield States
+enum
+{
+ SS_DOWN, // Not in use
+ SS_RAISING, // Going up
+ SS_UP, // Up, active, and blocking
+ SS_LOWERING, // Going down
+ SS_PARRYING, // In the process of parrying
+ SS_PARRYING_FINISH_SWING, // Time to allow the parrying animation to finish
+ SS_UNAVAILABLE, // Post-parry, unavailable for using
+};
+
+// Shield Hitboxes
+enum
+{
+ SS_HITBOX_NOSHIELD,
+ SS_HITBOX_FULLSHIELD,
+ SS_HITBOX_SMALLSHIELD,
+};
+
+// Range of the shield bash
+#define SHIELD_BASH_RANGE 64.0
+
+#if defined( CLIENT_DLL )
+class Beam_t;
+
+#define CWeaponCombatShield C_WeaponCombatShield
+#define CWeaponCombatShieldAlien C_WeaponCombatShieldAlien
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Shared version of CWeaponCombatShield
+//-----------------------------------------------------------------------------
+class CWeaponCombatShield : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponCombatShield, CBaseTFCombatWeapon );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatShield();
+
+ virtual void Precache( void );
+
+ virtual bool Deploy( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ virtual bool TakeShieldBash( CBaseTFPlayer *pBasher );
+ virtual void ItemPostFrame( void );
+ virtual void ItemBusyFrame( void );
+ virtual void ShieldPostFrame( void );
+ virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
+ virtual int UpdateClientData( CBasePlayer *pPlayer );
+
+ void SetShieldState( int iShieldState );
+ void UpdateShieldState( void );
+ int GetShieldState( void );
+
+ void ShieldBash( void );
+
+ void SetAllowPostFrame( bool allow );
+ void SetShieldUsable( bool bUsable );
+ bool ShieldUsable( void );
+
+ // Shield power levels
+ float AttemptToBlock( float flDamage );
+ void ShieldRechargeThink( void );
+
+ virtual bool VisibleInWeaponSelection( void );
+ virtual int GetOtherWeaponsActivity( int iActivity );
+ virtual int ReplaceOtherWeaponsActivity( int iActivity );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+ bool IsUp( void );
+ float GetRaisingTime( void );
+ float GetLoweringTime( void );
+
+ // Shield health handling
+ float GetShieldHealth( void );
+ void AddShieldHealth( float flHealth );
+ void RemoveShieldHealth( float flHealth );
+
+#if defined( CLIENT_DLL )
+ virtual void GetViewmodelBoneControllers( CBaseViewModel *pViewModel, float controllers[MAXSTUDIOBONECTRLS]);
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() &&
+ GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ virtual void DrawAmmo( void );
+ virtual void HandleInput( void );
+
+ virtual void ViewModelDrawn( C_BaseViewModel *pViewModel );
+
+ void InitShieldBeam( void );
+ void InitTeslaBeam( void );
+
+ void DrawBeams( C_BaseViewModel *pViewModel );
+
+#endif
+
+private:
+ CWeaponCombatShield( const CWeaponCombatShield& );
+
+ CNetworkVar( int, m_iShieldState );
+
+ bool m_bUsable;
+
+ CNetworkVar( float, m_flShieldUpStartTime );
+ CNetworkVar( float, m_flShieldDownStartTime );
+ CNetworkVar( float, m_flShieldParryEndTime );
+ CNetworkVar( float, m_flShieldParrySwingEndTime );
+ CNetworkVar( float, m_flShieldUnavailableEndTime );
+
+ CNetworkVar( float, m_flShieldRaisedTime );
+ CNetworkVar( float, m_flShieldLoweredTime );
+
+ CNetworkVar( float, m_flShieldHealth );
+
+ CNetworkVar( bool, m_bAllowPostFrame );
+ CNetworkVar( bool, m_bHasShieldParry );
+
+#if defined( CLIENT_DLL )
+ float m_flFlashTimeEnd;
+
+ float m_flTeslaSpeed;
+ float m_flTeslaSkitter;
+ float m_flTeslaLeftInc;
+ float m_flTeslaRightInc;
+ Beam_t *m_pTeslaBeam;
+ Beam_t *m_pTeslaBeam2;
+ CMaterialReference m_hTeslaSpriteMaterial;
+
+ bool m_bLeftToRight;
+ int m_nShieldEdge;
+ float m_flShieldSpeed;
+ float m_flShieldInc;
+ Beam_t *m_pShieldBeam;
+ Beam_t *m_pShieldBeam2;
+ Beam_t *m_pShieldBeam3;
+ CMaterialReference m_hShieldSpriteMaterial;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Need to do different art on client vs server
+//-----------------------------------------------------------------------------
+class CWeaponCombatShieldAlien : public CWeaponCombatShield
+{
+ DECLARE_CLASS( CWeaponCombatShieldAlien, CWeaponCombatShield );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCombatShieldAlien( void ) {}
+
+private:
+ CWeaponCombatShieldAlien( const CWeaponCombatShieldAlien & );
+};
+
+
+#endif // WEAPON_COMBATSHIELD_SHARED_H
diff --git a/game/shared/tf2/weapon_drainbeam.cpp b/game/shared/tf2/weapon_drainbeam.cpp
new file mode 100644
index 0000000..925fa15
--- /dev/null
+++ b/game/shared/tf2/weapon_drainbeam.cpp
@@ -0,0 +1,523 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Sapper's draining beam
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "in_buttons.h"
+#include "weapon_combatshield.h"
+#include "engine/IEngineSound.h"
+#include "grenade_base_empable.h"
+
+#if defined( CLIENT_DLL )
+
+#include "particles_simple.h"
+
+#else
+
+#include "tf_gamerules.h"
+#include "tf_class_sapper.h"
+
+#endif
+
+#include "weapon_drainbeam.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#define PARTICLE_PATH_VEL 140.0
+#define NUM_PATH_PARTICLES_PER_SEC 600.0f
+#define NUM_DRIBBLE_PARTICLES_PER_SEC 20.0f
+
+#define NUM_DRAINBEAM_PATH_POINTS 8
+
+// Buff ranges
+static ConVar weapon_drainbeam_target_range( "weapon_drainbeam_target_range", "900", FCVAR_REPLICATED, "The farthest away you can be for the drain gun to initially lock onto a target." );
+static ConVar weapon_drainbeam_stick_range( "weapon_drainbeam_stick_range", "612", FCVAR_REPLICATED, "How far away the drain gun can stay locked onto a target." );
+static ConVar weapon_drainbeam_max_rate( "weapon_drainbeam_max_rate", "10", FCVAR_REPLICATED );
+static ConVar weapon_drainbeam_max_rate_time( "weapon_drainbeam_max_rate_time", "5", FCVAR_REPLICATED );
+static ConVar weapon_drainbeam_emp_time_factor( "weapon_drainbeam_emp_time_factor", "0.75", FCVAR_REPLICATED );
+
+
+LINK_ENTITY_TO_CLASS( weapon_drainbeam, CWeaponDrainBeam );
+
+PRECACHE_WEAPON_REGISTER( weapon_drainbeam );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponDrainBeam, DT_WeaponDrainBeam )
+
+BEGIN_NETWORK_TABLE( CWeaponDrainBeam, DT_WeaponDrainBeam )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO( m_bDraining ), 1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_bAttacking ), 1, SPROP_UNSIGNED ),
+ SendPropVector( SENDINFO( m_vFireTarget ), 0, SPROP_COORD ),
+ SendPropEHandle( SENDINFO( m_hDrainTarget ) ),
+#else
+ RecvPropInt( RECVINFO( m_bAttacking ) ),
+ RecvPropInt( RECVINFO( m_bDraining ) ),
+ RecvPropVector( RECVINFO(m_vFireTarget) ),
+ RecvPropEHandle( RECVINFO(m_hDrainTarget) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponDrainBeam )
+
+ DEFINE_PRED_FIELD( m_bDraining, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bAttacking, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD_TOL( m_vFireTarget, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.125f ),
+
+ DEFINE_PRED_FIELD( m_hDrainTarget, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+
+// DEFINE_PRED_FIELD( m_pEmitter, FIELD_POINTER ),
+// DEFINE_PRED_FIELD( m_hParticleMaterial, FIELD_???, ),
+// DEFINE_PRED_FIELD( m_PathParticleEvent, FIELD_???, ),
+// DEFINE_PRED_FIELD( m_DribbleParticleEvent, FIELD_??? ),
+// DEFINE_PRED_FIELD( m_bPlayingSound, FIELD_BOOLEAN ),
+
+END_PREDICTION_DATA()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponDrainBeam::CWeaponDrainBeam( void )
+{
+ m_bDraining = false;
+ m_bAttacking = false;
+ m_flNextBuzzTime = 0;
+
+ SetPredictionEligible( true );
+#if defined( CLIENT_DLL )
+ m_pEmitter = NULL;
+ m_hParticleMaterial = INVALID_MATERIAL_HANDLE;
+ m_PathParticleEvent.Init( NUM_PATH_PARTICLES_PER_SEC );
+ m_DribbleParticleEvent.Init( NUM_DRIBBLE_PARTICLES_PER_SEC );
+ m_bPlayingSound = false;
+#endif
+}
+
+void CWeaponDrainBeam::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "WeaponRepairGun.Healing" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponDrainBeam::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ RemoveDrainTarget();
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a pointer to a drainable target
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponDrainBeam::GetTargetToDrain( CBaseEntity *pCurDrainTarget )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return NULL;
+
+#if !defined( CLIENT_DLL )
+ Vector vecSrc = pOwner->Weapon_ShootPosition( );
+
+ // ROBIN: Disabled drainbeam locking to target
+/*
+ // If we're already draining something, stick onto it as long as possible.
+ CBaseEntity *pEntity = pCurDrainTarget;
+ if ( pEntity && pEntity->IsAlive() )
+ {
+ // Make sure the target didn't go out of range.
+ Vector vecTargetCenter = pEntity->Center();
+ if ( (vecTargetCenter - vecSrc).Length() < weapon_drainbeam_stick_range.GetFloat() )
+ {
+ // Now make sure there isn't something other than team players in the way.
+ class CDrainFilter : public CTraceFilterSimple
+ {
+ public:
+ CDrainFilter( CBaseEntity *pShooter ) : CTraceFilterSimple( pShooter, TFCOLLISION_GROUP_WEAPON )
+ {
+ m_pShooter = pShooter;
+ }
+
+ virtual bool ShouldHitEntity( IServerEntity *pServerEntity, int contentsMask )
+ {
+ // If it hit an edict the isn't the target and is on our team, then the ray is blocked.
+ CBaseEntity *pEnt = (CBaseEntity *)pServerEntity;
+
+ // Ignore collisions with the shooter
+ if ( pEnt == m_pShooter )
+ return false;
+
+ // Ignore neutral objects
+ if ( pEnt->GetTeamNumber() == 0 )
+ return false;
+
+ // Ignore collisions with teammates
+ if ( pEnt->GetTeam() == m_pShooter->GetTeam() )
+ return false;
+
+ // Ignore players
+ if ( pEnt->IsPlayer() )
+ return false;
+
+ return CTraceFilterSimple::ShouldHitEntity( pServerEntity, contentsMask );
+ }
+
+ CBaseEntity *m_pShooter;
+ };
+
+ trace_t tr;
+ CDrainFilter drainFilter( pOwner );
+ engine->TraceLine( vecSrc, vecTargetCenter, MASK_SHOT, &drainFilter, &tr );
+
+ if (( tr.fraction == 1.0f) || (tr.m_pEnt == pEntity))
+ return pEntity;
+ }
+
+ // Return null so we can't target this player but m_hDrainTarget stays set to them.
+ return NULL;
+ }
+ else
+*/
+ {
+ // Ok, try to find a new target to drain.
+ // Get the target point and location
+ Vector vecAiming;
+ pOwner->EyeVectors( &vecAiming );
+
+ // Find a player in range of this player, and make sure they're drainable.
+ Vector vecEnd = vecSrc + vecAiming * weapon_drainbeam_target_range.GetFloat();
+ trace_t tr;
+
+ // Use WeaponTraceLine so shields are tested...
+ TFGameRules()->WeaponTraceLine( vecSrc, vecEnd, MASK_SHOT & (~CONTENTS_HITBOX), pOwner, DMG_PROBE, &tr );
+
+ if ( tr.fraction != 1.0 )
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if ( pEntity && (pEntity != pOwner) && pEntity->IsAlive() )
+ {
+ // Target needs to not be a player, take EMP damage, and needs to be an enemy
+ if ( !pEntity->IsPlayer() && pEntity->CanBePoweredUp() && pEntity->GetTeamNumber() != 0 && !pEntity->InSameTeam( pOwner ) )
+ {
+ // If we've just locked onto a new target, restart our drain rate
+ if ( pCurDrainTarget != pEntity )
+ {
+ m_flDrainStartedAt = gpGlobals->curtime;
+ }
+ return pEntity;
+ }
+ }
+ }
+
+ return NULL;
+ }
+#endif
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Overloaded to handle the hold-down draining
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Try to start draining
+ m_bAttacking = false;
+ if ( (pOwner->m_nButtons & IN_ATTACK) && GetShieldState() == SS_DOWN )
+ {
+ PrimaryAttack();
+ m_bAttacking = true;
+ }
+ else if ( GetCurDrainTarget() )
+ {
+ // Detach from the player if they release the attack button.
+ RemoveDrainTarget();
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're draining
+ AllowShieldPostFrame( !m_bAttacking );
+
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::RemoveDrainTarget( void )
+{
+ m_hDrainTarget = NULL;
+ m_bDraining = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Attempt to drain any player within range of the medikit
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+#if !defined( CLIENT_DLL )
+ // Find a target to drain
+ CBaseEntity *pTarget = GetTargetToDrain( GetCurDrainTarget() );
+ if ( pTarget )
+ {
+ // EMP the target
+ if ( pTarget->CanBePoweredUp() )
+ {
+ // Increase the EMP time based upon the time we spent draining
+ float flDrainTime = (gpGlobals->curtime - m_flDrainStartedAt);
+ float flPostDrainTime = weapon_drainbeam_emp_time_factor.GetFloat() * flDrainTime;
+ if ( pTarget->AttemptToPowerup( POWERUP_EMP, flPostDrainTime ) )
+ {
+ // Crank up the drain rate based on the time since we started draining this target
+ flDrainTime = MIN( weapon_drainbeam_max_rate_time.GetFloat(), flDrainTime );
+ float flDrainAmount = weapon_drainbeam_max_rate.GetFloat() * ( flDrainTime / weapon_drainbeam_max_rate_time.GetFloat() );
+
+ // It uses floating point in here so it doesn't lose the fractional part on small frame times.
+ static float flCurDamageAmt = 0.99;
+ float flOldDamageAmt = flCurDamageAmt;
+ flCurDamageAmt += gpGlobals->frametime * flDrainAmount;
+ int iDamage = (int)flCurDamageAmt - (int)flOldDamageAmt;
+
+ // Accumulated enough to do something?
+ if ( iDamage )
+ {
+ // EMPed my target, make it take damage
+ if ( pTarget->GetHealth() )
+ {
+ pTarget->TakeDamage( CTakeDamageInfo( this, pOwner, iDamage, DMG_GENERIC ) );
+ }
+
+ // Increase my health
+ if ( pOwner->GetHealth() < pOwner->GetMaxHealth() )
+ {
+ int maxHealthToAdd = pOwner->GetMaxHealth() - pOwner->GetHealth();
+ int nHealthAdded = MIN( iDamage, maxHealthToAdd );
+ pOwner->TakeHealth( nHealthAdded, DMG_GENERIC );
+ }
+
+ // If I'm a sapper, I store off the energy
+ if ( pOwner->IsClass( TFCLASS_SAPPER ) )
+ {
+ ((CPlayerClassSapper*)pOwner->GetPlayerClass())->AddDrainedEnergy( iDamage );
+ }
+ }
+ }
+ }
+
+ // Tell the client who we're trying to drain.
+ m_bDraining = true;
+ m_vFireTarget = pTarget->WorldSpaceCenter();
+ m_hDrainTarget = pTarget;
+ }
+ else
+ {
+ RemoveDrainTarget();
+ }
+#endif
+
+ // Fire continuously.
+ m_flNextPrimaryAttack = gpGlobals->curtime;
+
+ CheckRemoveDisguise();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::PlayAttackAnimation( int activity )
+{
+ SendWeaponAnim( activity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::WeaponIdle( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+}
+
+#if defined( CLIENT_DLL )
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::StopDrainSound( bool bStopHealingSound, bool bStopNoTargetSound )
+{
+ if ( bStopHealingSound )
+ StopSound( entindex(), "WeaponRepairGun.Healing" );
+
+ if ( bStopNoTargetSound )
+ StopSound( entindex(), "WeaponRepairGun.NoTarget" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ m_pEmitter = CSimpleEmitter::Create( "C_WeaponDrainBeam" );
+ m_hParticleMaterial = m_pEmitter->GetPMaterial( "sprites/chargeball" );
+
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS );
+ }
+
+ // Think?
+ if ( !m_bDraining )
+ {
+ m_bPlayingSound = false;
+ StopDrainSound( true, false );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponDrainBeam::ClientThink()
+{
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( !pPlayer )
+ return;
+
+ // Don't show it while the player is dead. Ideally, we'd respond to m_bDraining in OnDataChanged,
+ // but it stops sending the weapon when it's holstered, and it gets holstered when the player dies.
+ C_BasePlayer *pFiringPlayer = dynamic_cast< C_BasePlayer* >( GetOwner() );
+ if ( !pFiringPlayer || pFiringPlayer->IsPlayerDead() )
+ {
+ m_bPlayingSound = false;
+ StopDrainSound();
+ return;
+ }
+
+ // Abort if we're not draining, or the player doesn't have the fire button down
+ if ( !m_bDraining && !m_bAttacking )
+ return;
+
+ // Ready for particle emission, sir
+ m_pEmitter->SetSortOrigin( (m_vFireTarget + pPlayer->GetAbsOrigin()) * 0.5f );
+ float flCur = gpGlobals->frametime;
+ Vector vForward, vOrigin;
+ QAngle vAngles;
+ GetShootPosition( vOrigin, vAngles );
+ AngleVectors( vAngles, &vForward );
+
+ // Are they holding the attack button but not draining anyone? Give feedback.
+ if ( !m_bDraining )
+ {
+ // Add random short-lived particles from the gun tip to the target.
+ while ( m_DribbleParticleEvent.NextEvent( flCur ) )
+ {
+ Vector vPos = vOrigin;
+ vPos += RandomVector( -2, 2 );
+ SimpleParticle *pParticle = m_pEmitter->AddSimpleParticle( m_hParticleMaterial, vPos );
+ if ( pParticle )
+ {
+ // Move the points along the path.
+ pParticle->m_vecVelocity = vForward;
+ pParticle->m_vecVelocity *= 50;
+
+ pParticle->m_flRoll = 0;
+ pParticle->m_flRollDelta = 0;
+ pParticle->m_flDieTime = 0.4f;
+ pParticle->m_flLifetime = 0;
+ pParticle->m_uchColor[0] = 255;
+ pParticle->m_uchColor[1] = 255;
+ pParticle->m_uchColor[2] = 255;
+ pParticle->m_uchStartAlpha = 32;
+ pParticle->m_uchEndAlpha = 0;
+ pParticle->m_uchStartSize = 4;
+ pParticle->m_uchEndSize = 0;
+ pParticle->m_iFlags = 0;
+ }
+ }
+ return;
+ }
+
+ if ( !m_bPlayingSound )
+ {
+ m_bPlayingSound = true;
+ CLocalPlayerFilter filter;
+
+ EmitSound( filter, entindex(), "WeaponRepairGun.Healing" );
+ }
+
+ Vector points[NUM_DRAINBEAM_PATH_POINTS];
+
+ // First generate a sequence of points so we can parameterize the (curvy) path from the
+ // tip of the gun to the target.
+ Vector vDirTo = m_vFireTarget - vOrigin;
+ float flDistanceTo = vDirTo.Length();
+ vDirTo /= flDistanceTo;
+
+ float flBendDist = flDistanceTo * 3;
+ const Vector A = vOrigin - vForward * flBendDist;
+ const Vector &B = vOrigin;
+ const Vector &C = m_vFireTarget;
+ const Vector D = m_vFireTarget - vForward * flBendDist;
+
+ for ( int i=0; i < NUM_DRAINBEAM_PATH_POINTS; i++ )
+ {
+ Catmull_Rom_Spline( A, B, C, D, (float)i / (NUM_DRAINBEAM_PATH_POINTS-1), points[i] );
+ }
+
+ // Add random short-lived particles from the gun tip to the target.
+ while ( m_PathParticleEvent.NextEvent( flCur ) )
+ {
+ float t = RandomFloat( 0, 1 );
+ int iPrev = (int)( t * (NUM_DRAINBEAM_PATH_POINTS - 1.001) );
+ float tPrev = (float)iPrev / (NUM_DRAINBEAM_PATH_POINTS - 1);
+ float tNext = (float)(iPrev+1) / (NUM_DRAINBEAM_PATH_POINTS - 1);
+ Assert( tNext <= NUM_DRAINBEAM_PATH_POINTS-1 );
+
+ Vector vPos;
+ VectorLerp( points[iPrev], points[iPrev+1], (t-tPrev) / (tNext - tPrev), vPos );
+ vPos += RandomVector( -2, 2 );
+
+ SimpleParticle *pParticle = m_pEmitter->AddSimpleParticle( m_hParticleMaterial, vPos );
+ if ( pParticle )
+ {
+ // Move the points along the path.
+ pParticle->m_vecVelocity = points[iPrev+1] - points[iPrev];
+ VectorNormalize( pParticle->m_vecVelocity );
+ pParticle->m_vecVelocity *= -PARTICLE_PATH_VEL;
+
+ pParticle->m_flRoll = 0;
+ pParticle->m_flRollDelta = 0;
+ pParticle->m_flDieTime = 0.2f;
+ pParticle->m_flLifetime = 0;
+ pParticle->m_uchColor[0] = 255;
+ pParticle->m_uchColor[1] = 255;
+ pParticle->m_uchColor[2] = 255;
+ pParticle->m_uchStartAlpha = 32;
+ pParticle->m_uchEndAlpha = 0;
+ pParticle->m_uchStartSize = 4;
+ pParticle->m_uchEndSize = 2;
+ pParticle->m_iFlags = 0;
+ }
+ }
+}
+#endif
diff --git a/game/shared/tf2/weapon_drainbeam.h b/game/shared/tf2/weapon_drainbeam.h
new file mode 100644
index 0000000..61239ce
--- /dev/null
+++ b/game/shared/tf2/weapon_drainbeam.h
@@ -0,0 +1,99 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_DRAINBEAM_H
+#define WEAPON_DRAINBEAM_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "weapon_combat_usedwithshieldbase.h"
+
+#if defined( CLIENT_DLL )
+#define CWeaponDrainBeam C_WeaponDrainBeam
+#endif
+
+//=========================================================
+// Medikit Weapon
+//=========================================================
+class CWeaponDrainBeam : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponDrainBeam, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponDrainBeam( void );
+
+ virtual void Precache();
+
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual void WeaponIdle( void );
+
+ virtual void PlayAttackAnimation( int activity );
+ virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
+
+ // Draining
+ CBaseEntity *GetTargetToDrain( CBaseEntity *pCurDrainTarget );
+ CBaseEntity *GetCurDrainTarget( void ) { return m_hDrainTarget; }
+ void RemoveDrainTarget( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+ // Stop all sounds being output.
+ void StopDrainSound( bool bStopHealingSound = true, bool bStopNoTargetSound = true );
+// IClientNetworkable.
+public:
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+// IClientThinkable.
+public:
+
+ virtual void ClientThink();
+
+#endif
+
+public:
+ CNetworkHandle( CBaseEntity, m_hDrainTarget );
+
+// Networked data.
+public:
+ CNetworkVar( bool, m_bDraining );
+ CNetworkVar( bool, m_bAttacking );
+ CNetworkVector( m_vFireTarget );
+
+#if defined( CLIENT_DLL )
+ CSmartPtr<CSimpleEmitter> m_pEmitter;
+ PMaterialHandle m_hParticleMaterial;
+
+ TimedEvent m_PathParticleEvent;
+ TimedEvent m_DribbleParticleEvent;
+
+ bool m_bPlayingSound;
+#else
+ float m_flDrainStartedAt;
+#endif
+private:
+ double m_flNextBuzzTime;
+
+ CWeaponDrainBeam( const CWeaponDrainBeam & );
+};
+
+#endif // WEAPON_DRAINBEAM_H
diff --git a/game/shared/tf2/weapon_flame_thrower.cpp b/game/shared/tf2/weapon_flame_thrower.cpp
new file mode 100644
index 0000000..9d98227
--- /dev/null
+++ b/game/shared/tf2/weapon_flame_thrower.cpp
@@ -0,0 +1,434 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "in_buttons.h"
+#include "weapon_combatshield.h"
+#include "weapon_flame_thrower.h"
+#include "gasoline_shared.h"
+#include "ammodef.h"
+
+
+#define FLAME_THROWER_FIRE_INTERVAL 0.3 // Eject a fire blob entity this often.
+
+#define FLAMETHROWER_FLAME_DISTANCE 400.0
+
+#define FLAMETHROWER_FLAME_SPEED 500.0 //
+
+#define FLAMETHROWER_DAMAGE_PER_SEC 1000
+
+// How far the flame particles will spread from the center.
+#define FLAMETHROWER_SPREAD_ANGLE 15.0
+
+
+// ------------------------------------------------------------------------------------------------ //
+// Pretty little tables.
+// ------------------------------------------------------------------------------------------------ //
+
+#if defined( CLIENT_DLL )
+
+ #include "vstdlib/random.h"
+ #include "engine/IEngineSound.h"
+
+ #define FLAMETHROWER_PARTICLES_PER_SEC 100
+
+#else
+
+ #include "gasoline_blob.h"
+ #include "fire_damage_mgr.h"
+ #include "tf_gamerules.h"
+
+ #define FLAMETHROWER_DAMAGE_INTERVAL 0.2
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+PRECACHE_WEAPON_REGISTER( weapon_flame_thrower );
+
+LINK_ENTITY_TO_CLASS( weapon_flame_thrower, CWeaponFlameThrower );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFlameThrower, DT_WeaponFlameThrower )
+
+BEGIN_NETWORK_TABLE( CWeaponFlameThrower, DT_WeaponFlameThrower )
+ #if defined( CLIENT_DLL )
+ RecvPropInt( RECVINFO( m_bFiring ) )
+ #else
+ SendPropInt( SENDINFO( m_bFiring ), 1, SPROP_UNSIGNED )
+ #endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponFlameThrower )
+
+ DEFINE_PRED_FIELD( m_bFiring, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+
+static inline void GenerateRandomFlameThrowerVelocity( Vector &vOut, const Vector &vForward, const Vector &vRight, const Vector &vUp )
+{
+ static float radians = DEG2RAD( 90 - FLAMETHROWER_SPREAD_ANGLE );
+ static float v = cos( radians ) / sin( radians );
+
+ vOut = vForward + vRight * RandomFloat( -v, v ) + vUp * RandomFloat( -v, v );
+ VectorNormalize( vOut );
+}
+
+template< class T >
+int FindInArray( const T pTest, const T *pArray, int arrayLen )
+{
+ for ( int i=0; i < arrayLen; i++ )
+ {
+ if ( pTest == pArray[i] )
+ return i;
+ }
+ return -1;
+}
+
+
+// ------------------------------------------------------------------------------------------------ //
+// CWeaponFlameThrower implementation.
+// ------------------------------------------------------------------------------------------------ //
+
+CWeaponFlameThrower::CWeaponFlameThrower()
+{
+ InternalConstructor( false );
+}
+
+
+CWeaponFlameThrower::CWeaponFlameThrower( bool bCanister )
+{
+ InternalConstructor( bCanister );
+}
+
+void CWeaponFlameThrower::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "FlameThrower.Sound" );
+}
+
+void CWeaponFlameThrower::InternalConstructor( bool bCanister )
+{
+ m_bCanister = bCanister;
+
+ m_bFiring = false;
+ m_flNextPrimaryAttack = -1;
+
+ #if defined( CLIENT_DLL )
+ {
+ m_hFlameEmitter = CSimpleEmitter::Create( "flamethrower" );
+
+ m_hFireMaterial = INVALID_MATERIAL_HANDLE;
+ if ( IsGasCanister() )
+ {
+ m_hFireMaterial = m_hFlameEmitter->GetPMaterial( "particle/particle_noisesphere" );
+ }
+ else
+ {
+ m_hFireMaterial = m_hFlameEmitter->GetPMaterial( "particle/fire" );
+ }
+
+ m_FlameEvent.Init( FLAMETHROWER_PARTICLES_PER_SEC );
+
+ m_bSoundOn = false;
+ }
+ #endif
+}
+
+
+CWeaponFlameThrower::~CWeaponFlameThrower()
+{
+ #if defined( CLIENT_DLL )
+ StopFlameSound();
+ #endif
+}
+
+
+bool CWeaponFlameThrower::IsGasCanister() const
+{
+ return m_bCanister;
+}
+
+
+bool CWeaponFlameThrower::IsPredicted() const
+{
+ return true;
+}
+
+
+void CWeaponFlameThrower::ItemPostFrame()
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ if ( pOwner->IsAlive() &&
+ (pOwner->m_nButtons & IN_ATTACK) &&
+ GetShieldState() == SS_DOWN &&
+ GetPrimaryAmmo() > 2 )
+ {
+ PrimaryAttack();
+ m_bFiring = true;
+
+ // Prevent shield post frame if we're not ready to attack, or we're healing
+ AllowShieldPostFrame( false );
+ }
+ else
+ {
+ AllowShieldPostFrame( true );
+ m_flNextPrimaryAttack = -1;
+ m_bFiring = false;
+
+#if defined( CLIENT_DLL )
+#else
+ m_hPrevBlob = NULL;
+
+ // It's easy to lay down gasoline and forget to leave enough ammo to ignite it, so
+ // this allows the pyro to ignite any nearby gasoline blobs for free.
+ if ( !m_bCanister && GetPrimaryAmmo() <= 2 )
+ {
+ IgniteNearbyGasolineBlobs();
+ }
+#endif
+ }
+}
+
+
+void CWeaponFlameThrower::PrimaryAttack()
+{
+ #if defined( CLIENT_DLL )
+
+ #else
+
+ CBasePlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Ok.. find eligible entities in a cone in front of us.
+ Vector vOrigin = pOwner->Weapon_ShootPosition( );
+ Vector vForward, vRight, vUp;
+ AngleVectors( pOwner->GetAbsAngles(), &vForward, &vRight, &vUp );
+
+ // Find some entities to burn.
+ CBaseEntity *pHitEnts[64];
+ int nHitEnts = 0;
+
+ #define NUM_TEST_VECTORS 30
+ for ( int iTest=0; iTest < NUM_TEST_VECTORS; iTest++ )
+ {
+ Vector vVel;
+ GenerateRandomFlameThrowerVelocity( vVel, vForward, vRight, vUp );
+
+ trace_t tr;
+ UTIL_TraceLine( vOrigin, vOrigin + vVel * FLAMETHROWER_FLAME_DISTANCE, MASK_SHOT & (~CONTENTS_HITBOX), NULL, COLLISION_GROUP_NONE, &tr );
+ if ( tr.m_pEnt )
+ {
+ if ( TFGameRules()->IsTraceBlockedByWorldOrShield( vOrigin, vOrigin + vVel * FLAMETHROWER_FLAME_DISTANCE, GetOwner(), DMG_BURN | DMG_PROBE, &tr ) == false )
+ {
+ CBaseEntity *pTestEnt = tr.m_pEnt;
+ if ( pTestEnt && IsBurnableEnt( pTestEnt, GetTeamNumber() ) )
+ {
+ if ( FindInArray( pTestEnt, pHitEnts, nHitEnts ) == -1 )
+ {
+ pHitEnts[nHitEnts++] = pTestEnt;
+ if ( nHitEnts >= ARRAYSIZE( pHitEnts ) )
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for ( int iHitEnt=0; iHitEnt < nHitEnts; iHitEnt++ )
+ {
+ CBaseEntity *pEnt = pHitEnts[iHitEnt];
+
+ float flDist = (pEnt->GetAbsOrigin() - vOrigin).Length();
+ float flPercent = 1.0 - flDist / FLAMETHROWER_FLAME_DISTANCE;
+ if ( flPercent < 0.1 )
+ flPercent = 0.1;
+
+ float flDamage = flPercent * FLAMETHROWER_DAMAGE_PER_SEC;
+ GetFireDamageMgr()->AddDamage( pEnt, GetOwner(), flDamage, !IsGasolineBlob( pEnt ) );
+ }
+
+
+ // Drop a new petrol blob.
+ if ( gpGlobals->curtime >= m_flNextPrimaryAttack )
+ {
+ float flLifetime = MAX_LIT_GASOLINE_BLOB_LIFETIME;
+ if ( IsGasCanister() )
+ flLifetime = MAX_UNLIT_GASOLINE_BLOB_LIFETIME;
+
+ CGasolineBlob *pBlob = CGasolineBlob::Create( GetOwner(), vOrigin, vForward * FLAMETHROWER_FLAME_SPEED, false, FLAMETHROWER_FLAME_DISTANCE / FLAMETHROWER_FLAME_SPEED, flLifetime );
+ if ( pBlob )
+ {
+ if ( IsGasCanister() )
+ {
+ // Link the previous blob to this one.
+ pBlob->AddAutoBurnBlob( m_hPrevBlob );
+ if ( m_hPrevBlob.Get() )
+ m_hPrevBlob->AddAutoBurnBlob( pBlob );
+
+ m_hPrevBlob = pBlob;
+ }
+ else
+ {
+ pBlob->SetLit( true );
+ }
+ }
+
+ pOwner->RemoveAmmo( 2, m_iPrimaryAmmoType );
+
+ // Drop a blob every half second.
+ m_flNextPrimaryAttack = gpGlobals->curtime + FLAME_THROWER_FIRE_INTERVAL;
+ }
+
+ #endif
+}
+
+
+#if defined( CLIENT_DLL )
+
+ bool CWeaponFlameThrower::ShouldPredict()
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+
+ void CWeaponFlameThrower::NotifyShouldTransmit( ShouldTransmitState_t state )
+ {
+ BaseClass::NotifyShouldTransmit( state );
+
+ if ( state == SHOULDTRANSMIT_START )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+ else if ( state == SHOULDTRANSMIT_END )
+ {
+ SetNextClientThink( CLIENT_THINK_NEVER );
+ StopFlameSound();
+ }
+ }
+
+ void CWeaponFlameThrower::ClientThink()
+ {
+ // Spew some particles out.
+ if ( !IsDormant() && IsCarrierAlive() && m_bFiring && m_hFlameEmitter.IsValid() )
+ {
+ unsigned char color[4] = { 255, 128, 0, 255 };
+ if ( IsGasCanister() )
+ {
+ color[0] = color[1] = color[2] = 200;
+ color[3] = 255;
+ }
+
+ StartSound();
+
+ Vector vForward, vUp, vRight, vOrigin;
+ QAngle vAngles;
+ GetShootPosition( vOrigin, vAngles );
+ AngleVectors( vAngles, &vForward, &vRight, &vUp );
+
+ // Spew out flame particles.
+ float dt = gpGlobals->frametime;
+ while ( m_FlameEvent.NextEvent( dt ) )
+ {
+ SimpleParticle *p = m_hFlameEmitter->AddSimpleParticle(
+ m_hFireMaterial,
+ vOrigin + RandomVector( -3, 3 ),
+ FLAMETHROWER_FLAME_DISTANCE / FLAMETHROWER_FLAME_SPEED, // lifetime,
+ 9 // size
+ );
+
+ if ( p )
+ {
+ p->m_uchColor[0] = color[0];
+ p->m_uchColor[1] = color[1];
+ p->m_uchColor[2] = color[2];
+ p->m_uchStartAlpha = color[3];
+ p->m_uchEndAlpha = 0;
+ GenerateRandomFlameThrowerVelocity( p->m_vecVelocity, vForward, vRight, vUp );
+ p->m_vecVelocity *= RandomFloat( FLAMETHROWER_FLAME_SPEED * 0.9, FLAMETHROWER_FLAME_SPEED * 1.1 );
+ }
+ }
+ }
+ else
+ {
+ StopFlameSound();
+ }
+ }
+
+
+ void CWeaponFlameThrower::StartSound()
+ {
+ if ( !m_bSoundOn )
+ {
+ CLocalPlayerFilter filter;
+ EmitSound( filter, entindex(), "FlameThrower.Sound" );
+
+ m_bSoundOn = true;
+ }
+ }
+
+
+ void CWeaponFlameThrower::StopFlameSound()
+ {
+ if ( m_bSoundOn )
+ {
+ StopSound( entindex(), "FlameThrower.Sound" );
+ m_bSoundOn = false;
+ }
+ }
+
+
+#else
+
+ bool CWeaponFlameThrower::Holster( CBaseCombatWeapon *pSwitchingTo )
+ {
+ m_bFiring = false;
+
+ return BaseClass::Holster( pSwitchingTo );
+ }
+
+
+ void CWeaponFlameThrower::IgniteNearbyGasolineBlobs()
+ {
+ CBasePlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ Vector vOrigin = pOwner->Weapon_ShootPosition( );
+ CBaseEntity *ents[128];
+ float dists[128];
+ int nEnts = FindBurnableEntsInSphere(
+ ents,
+ dists,
+ ARRAYSIZE( dists ),
+ vOrigin,
+ 50,
+ pOwner );
+
+ for ( int i=0; i < nEnts; i++ )
+ {
+ CGasolineBlob *pBlob = dynamic_cast< CGasolineBlob* >( ents[i] );
+ if ( pBlob )
+ {
+ GetFireDamageMgr()->AddDamage( pBlob, pOwner, 500, false );
+ }
+ }
+ }
+
+#endif
+
+
diff --git a/game/shared/tf2/weapon_flame_thrower.h b/game/shared/tf2/weapon_flame_thrower.h
new file mode 100644
index 0000000..f2700e2
--- /dev/null
+++ b/game/shared/tf2/weapon_flame_thrower.h
@@ -0,0 +1,95 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_FLAME_THROWER_H
+#define WEAPON_FLAME_THROWER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "weapon_combat_usedwithshieldbase.h"
+
+#if defined( CLIENT_DLL )
+ #define CWeaponFlameThrower C_WeaponFlameThrower
+
+ #include "particlemgr.h"
+ #include "particle_util.h"
+ #include "particles_simple.h"
+
+#else
+
+ class CGasolineBlob;
+
+#endif
+
+//=========================================================
+// Medikit Weapon
+//=========================================================
+class CWeaponFlameThrower : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponFlameThrower, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponFlameThrower();
+ CWeaponFlameThrower( bool bCanister );
+ ~CWeaponFlameThrower();
+
+ virtual void Precache();
+
+ // The gas canister says yes so we can do different things.
+ bool IsGasCanister() const;
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const;
+ virtual void ItemPostFrame();
+
+
+#if defined( CLIENT_DLL )
+
+ virtual bool ShouldPredict( void );
+
+ virtual void NotifyShouldTransmit( ShouldTransmitState_t state );
+ virtual void ClientThink();
+
+ // Start/stop the fire sound.
+ void StartSound();
+ void StopFlameSound();
+ bool m_bSoundOn; // Is the sound on?
+
+ CSmartPtr<CSimpleEmitter> m_hFlameEmitter;
+ PMaterialHandle m_hFireMaterial;
+ TimedEvent m_FlameEvent;
+
+#else
+
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ void IgniteNearbyGasolineBlobs();
+
+private:
+
+ // Used to link the blobs together.
+ CHandle<CGasolineBlob> m_hPrevBlob;
+
+
+#endif
+
+
+private:
+
+ void PrimaryAttack();
+ void InternalConstructor( bool bCanister );
+
+ CNetworkVar( bool, m_bFiring );
+ bool m_bCanister; // Tells if we're a gas canister or a flamethrower (both act in similar ways).
+ float m_flNextPrimaryAttack;
+
+ CWeaponFlameThrower( const CWeaponFlameThrower & );
+};
+
+#endif // WEAPON_FLAME_THROWER_H
diff --git a/game/shared/tf2/weapon_gas_can.cpp b/game/shared/tf2/weapon_gas_can.cpp
new file mode 100644
index 0000000..28684b1
--- /dev/null
+++ b/game/shared/tf2/weapon_gas_can.cpp
@@ -0,0 +1,35 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "in_buttons.h"
+#include "weapon_combatshield.h"
+#include "weapon_gas_can.h"
+
+
+LINK_ENTITY_TO_CLASS( weapon_gas_can, CWeaponGasCan );
+
+PRECACHE_WEAPON_REGISTER( weapon_flame_thrower );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGasCan, DT_WeaponGasCan )
+
+BEGIN_NETWORK_TABLE( CWeaponGasCan, DT_WeaponGasCan )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponGasCan )
+END_PREDICTION_DATA()
+
+
+// ------------------------------------------------------------------------------------------------ //
+// CWeaponGasCan implementation.
+// ------------------------------------------------------------------------------------------------ //
+
+CWeaponGasCan::CWeaponGasCan() : CWeaponFlameThrower( true )
+{
+}
+
+
diff --git a/game/shared/tf2/weapon_gas_can.h b/game/shared/tf2/weapon_gas_can.h
new file mode 100644
index 0000000..2df1850
--- /dev/null
+++ b/game/shared/tf2/weapon_gas_can.h
@@ -0,0 +1,39 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_GAS_CAN_H
+#define WEAPON_GAS_CAN_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "weapon_combat_usedwithshieldbase.h"
+#include "weapon_flame_thrower.h"
+
+#if defined( CLIENT_DLL )
+#define CWeaponGasCan C_WeaponGasCan
+#endif
+
+//=========================================================
+// Medikit Weapon
+//=========================================================
+class CWeaponGasCan : public CWeaponFlameThrower
+{
+ DECLARE_CLASS( CWeaponGasCan, CWeaponFlameThrower );
+public:
+ CWeaponGasCan();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+
+private:
+
+ CWeaponGasCan( const CWeaponGasCan & );
+};
+
+#endif // WEAPON_GAS_CAN_H
diff --git a/game/shared/tf2/weapon_grenade_rocket.cpp b/game/shared/tf2/weapon_grenade_rocket.cpp
new file mode 100644
index 0000000..2f32f56
--- /dev/null
+++ b/game/shared/tf2/weapon_grenade_rocket.cpp
@@ -0,0 +1,390 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Rockets (Weapon)
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_grenade_rocket.h"
+
+#if defined( CLIENT_DLL )
+// Client Only
+#include "hud.h"
+#include "particles_simple.h"
+#else
+// Server Only
+#include "gameinterface.h"
+#include "engine/IEngineSound.h"
+#include "explode.h"
+#include "tf_team.h"
+#include "env_laserdesignation.h"
+#include "iservervehicle.h"
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_grenade_rocket, CWeaponGrenadeRocket );
+BEGIN_PREDICTION_DATA( CWeaponGrenadeRocket )
+END_PREDICTION_DATA()
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGrenadeRocket, DT_WeaponGrenadeRocket )
+BEGIN_NETWORK_TABLE( CWeaponGrenadeRocket, DT_WeaponGrenadeRocket )
+//#if !defined( CLIENT_DLL )
+//#else
+//#endif
+END_NETWORK_TABLE()
+
+#define WEAPON_GRENADE_ROCKET_VELOCITY 1000
+
+#if !defined( CLIENT_DLL )
+// Server Only
+ConVar weapon_grenade_rocket_track_range_mod( "weapon_grenade_rocket_track_range_mod","1.5", FCVAR_NONE, "Range multiplier when a rocket's tracking a designated target." );
+ConVar weapon_grenade_rocket_force( "weapon_grenade_rocket_force","150.0", FCVAR_NONE, "Rocket force modifier." );
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CWeaponGrenadeRocket::CWeaponGrenadeRocket()
+{
+ m_flDamage = 100.0f;
+
+#if !defined( CLIENT_DLL )
+ // Server Only
+ UseClientSideAnimation();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deconstructor
+//-----------------------------------------------------------------------------
+CWeaponGrenadeRocket::~CWeaponGrenadeRocket()
+{
+#if defined( CLIENT_DLL )
+ StopSound( entindex(), "GrenadeRocket.FlyLoop" );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a weapon grenade rocket
+//-----------------------------------------------------------------------------
+CWeaponGrenadeRocket *CWeaponGrenadeRocket::Create( const Vector &vecOrigin, const Vector &vecForward, float flMaxRange, CBaseEntity *pOwner )
+{
+#if !defined( CLIENT_DLL )
+ CWeaponGrenadeRocket *pRocket = ( CWeaponGrenadeRocket* )CreateEntityByName( "weapon_grenade_rocket" );
+
+ UTIL_SetOrigin( pRocket, vecOrigin );
+ QAngle angles;
+ VectorAngles( vecForward, angles );
+ pRocket->SetLocalAngles( angles );
+ pRocket->Spawn();
+ pRocket->SetOwnerEntity( pOwner );
+ pRocket->ChangeTeam( pOwner->GetTeamNumber() );
+ pRocket->SetMaxRange( flMaxRange );
+
+ return pRocket;
+#else
+ return NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::SetMaxRange( float flRange )
+{
+ m_flMaxRange = flRange;
+ m_flFallingSpeed = 200; // Initial falling speed
+
+ // Reduce max range for upward shots
+ Vector vecForward;
+ GetVectors( &vecForward, NULL, NULL );
+ if ( vecForward.z > 0 )
+ {
+ m_flMaxRange = MAX(1, m_flMaxRange - (vecForward.z * 1200));
+ }
+ else
+ {
+ m_flMaxRange -= (vecForward.z * 2400);
+ }
+
+#if !defined( CLIENT_DLL )
+ if ( m_flMaxRange )
+ {
+ float flSpeed = GetLocalVelocity().Length();
+ Assert( flSpeed );
+ m_flExceedRangeTime = gpGlobals->curtime + (m_flMaxRange / flSpeed);
+
+ // Start looking for designators
+ SetThink( TrackThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::Spawn( void )
+{
+#if !defined( CLIENT_DLL )
+ Precache();
+
+ m_flRadius = 100;
+ SetMoveType( MOVETYPE_FLY );
+ SetSolid( SOLID_BBOX );
+ SetModel( "models/weapons/w_missile.mdl" );
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+
+ SetCollisionGroup( TFCOLLISION_GROUP_WEAPON );
+
+ // Forward!
+ Vector forward;
+ AngleVectors( GetLocalAngles(), &forward, NULL, NULL );
+ SetAbsVelocity( forward * WEAPON_GRENADE_ROCKET_VELOCITY );
+
+ SetTouch( RocketTouch );
+#else
+ // Start our flying sound loop
+ CPASAttenuationFilter filter( this );
+ filter.MakeReliable();
+ EmitSound( filter, entindex(), "GrenadeRocket.FlyLoop" );
+#endif
+}
+
+#if !defined( CLIENT_DLL )
+// Server Only
+
+//-----------------------------------------------------------------------------
+// Purpose: Return my owner as my scorer
+//-----------------------------------------------------------------------------
+CBasePlayer *CWeaponGrenadeRocket::GetScorer( void )
+{
+ return ToBasePlayer( m_hOwner );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::Precache( void )
+{
+ PrecacheModel( "models/weapons/w_missile.mdl" );
+
+ PrecacheScriptSound( "GrenadeRocket.FlyLoop" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: We've exceeded this rocket's range, start heading downward, randomly
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::ExceededRangeThink( void )
+{
+ Vector vecZ( 0,0,1 );
+ Vector vecPerp;
+
+ // Weave drunkely and head down
+ Vector vecVelocity = GetLocalVelocity();
+ CrossProduct( vecVelocity, vecZ, vecPerp );
+ VectorNormalize( vecPerp );
+ vecPerp *= random->RandomFloat(-100,100);
+ vecVelocity += vecPerp;
+ vecVelocity.z -= m_flFallingSpeed;
+ m_flFallingSpeed += 50;
+ SetLocalVelocity( vecVelocity );
+
+ SetAnglesToMatchVelocity();
+ SetNextThink( gpGlobals->curtime + 0.3 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Track towards my my designated target
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::TrackThink( void )
+{
+ SetNextThink( gpGlobals->curtime + 0.3 );
+
+ // Have I exceeded my range?
+ if ( gpGlobals->curtime > m_flExceedRangeTime )
+ {
+ SetThink( ExceededRangeThink );
+ // Start falling immediately
+ ExceededRangeThink();
+ return;
+ }
+
+ // Look for any laser designators in tracking view
+ int iTeam = GetTeamNumber();
+ int iCount = CEnvLaserDesignation::GetNumLaserDesignators( iTeam );
+ if ( !iCount )
+ return;
+
+ // Get the potential lock range
+ float flIncreasedMaxRange = m_flMaxRange * weapon_grenade_rocket_track_range_mod.GetFloat();
+ float flNearestDot = 0.95;
+ Vector vecNearestTarget = vec3_origin;
+ bool bFoundOne = false;
+
+ // Any valid designated targets?
+ for ( int i = 0; i < iCount; i++ )
+ {
+ Vector vecTarget;
+ if ( !CEnvLaserDesignation::GetLaserDesignation( iTeam, i, &vecTarget ) )
+ continue;
+
+ // Check validity of designated target
+ Vector vecToTarget = ( vecTarget - GetAbsOrigin() );
+ float flDistanceSqr = vecToTarget.LengthSqr();
+ // Make sure it's not too far
+ if ( flDistanceSqr > (flIncreasedMaxRange*flIncreasedMaxRange) )
+ continue;
+
+ // Make sure it's near my flight path
+ VectorNormalize(vecToTarget);
+ Vector vecForward;
+ GetVectors( &vecForward, NULL, NULL );
+ float flDot = DotProduct( vecToTarget, vecForward );
+ if ( flDot < flNearestDot )
+ continue;
+
+ flNearestDot = flDot;
+ vecNearestTarget = vecTarget;
+ bFoundOne = true;
+ }
+
+ // No valid targets
+ if ( !bFoundOne )
+ return;
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ // Turn towards my target
+ Vector vecToTarget = (vecNearestTarget - GetAbsOrigin());
+ VectorNormalize(vecToTarget);
+
+ // Shamelessly ripped from HL1 RPG
+ float flSpeed = GetAbsVelocity().Length();
+ SetAbsVelocity( (GetAbsVelocity() * 0.5) + (vecToTarget * flSpeed * 0.498) );
+
+ SetAnglesToMatchVelocity();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set angles to match our velocity
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::SetAnglesToMatchVelocity( void )
+{
+ QAngle angles;
+ VectorAngles( GetAbsVelocity(), angles );
+ SetLocalAngles( angles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::RocketTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+ if ( !pOther->IsSolid() )
+ return;
+
+ // Apply forces to vehicles.
+ if ( pOther->GetServerVehicle() )
+ {
+ ApplyForcesToVehicle( pOther );
+ }
+
+ CPASFilter filter( GetAbsOrigin() );
+ te->Explosion( filter, 0.0, &GetAbsOrigin(), g_sModelIndexFireball, 2.0, 15, TE_EXPLFLAG_NONE, 100, m_flDamage );
+
+ // Use the owner's position as the reported position
+ Vector vecReported = vec3_origin;
+ if ( GetOwnerEntity() )
+ {
+ vecReported = GetOwnerEntity()->GetAbsOrigin();
+ }
+ RadiusDamage( CTakeDamageInfo( this, GetOwnerEntity(), vec3_origin, GetAbsOrigin(), GetDamage(), GetDamageType(), 0, &vecReported ), GetAbsOrigin(), GetDamageRadius(), CLASS_NONE, NULL );
+
+ UTIL_Remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::ApplyForcesToVehicle( CBaseEntity *pEntity )
+{
+ // Check team - don't apply forces to our own team's vehicles.
+ if ( pEntity->GetTeam() == GetTeam() )
+ return;
+
+ IServerVehicle *pVehicle = pEntity->GetServerVehicle();
+ if ( !pVehicle )
+ return;
+
+ IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject();
+ if ( pPhysObject )
+ {
+ //------------------------------------------------------------
+ // Rocket the vehicle in the direction of the incoming rocket.
+ //------------------------------------------------------------
+ Vector vecForceDir = GetAbsVelocity();
+ vecForceDir.z = 0.0f;
+ VectorNormalize( vecForceDir );
+
+ float flForce = pPhysObject->GetMass();
+ flForce += ( 4.0f * 100.0f ); // Wheels
+ flForce *= weapon_grenade_rocket_force.GetFloat();
+
+ vecForceDir *= flForce;
+
+ pPhysObject->ApplyForceOffset( vecForceDir, GetAbsOrigin() );
+ }
+}
+
+#else
+// Client Only
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ // Only think when "rocketing."
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Spawn rocket effects!
+//-----------------------------------------------------------------------------
+void CWeaponGrenadeRocket::ClientThink( void )
+{
+ // Fire smoke puffs out the side
+ CSmartPtr<CSimpleEmitter> pSmokeEmitter = CSimpleEmitter::Create( "C_GrenadeRocket::Effect" );
+ pSmokeEmitter->SetSortOrigin( GetAbsOrigin() );
+ PMaterialHandle hSphereMaterial = pSmokeEmitter->GetPMaterial( "particle/particle_noisesphere" );
+ int iSmokeClouds = random->RandomInt( 1,2 );
+ for ( int i = 0; i < iSmokeClouds; i++ )
+ {
+ SimpleParticle *pParticle = ( SimpleParticle* ) pSmokeEmitter->AddParticle( sizeof( SimpleParticle ), hSphereMaterial, GetAbsOrigin() );
+ if ( !pParticle )
+ return;
+
+ // Particle data.
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = random->RandomFloat( 0.1f, 0.3f );
+
+ pParticle->m_uchStartSize = 10;
+ pParticle->m_uchEndSize = pParticle->m_uchStartSize + 2;
+
+ pParticle->m_vecVelocity = GetAbsVelocity();
+ pParticle->m_uchStartAlpha = 255;
+ pParticle->m_uchEndAlpha = 64;
+ pParticle->m_flRoll = random->RandomFloat( 180, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -1, 1 );
+
+ pParticle->m_uchColor[0] = 50;
+ pParticle->m_uchColor[1] = 250;
+ pParticle->m_uchColor[2] = 50;
+ }
+}
+
+#endif
+
diff --git a/game/shared/tf2/weapon_grenade_rocket.h b/game/shared/tf2/weapon_grenade_rocket.h
new file mode 100644
index 0000000..32bdf8c
--- /dev/null
+++ b/game/shared/tf2/weapon_grenade_rocket.h
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Rockets (Weapon)
+//
+//=============================================================================//
+
+#ifndef WEAPON_GRENADE_ROCKET_H
+#define WEAPON_GRENADE_ROCKET_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#if defined( CLIENT_DLL )
+// Client Only
+ #define CWeaponGrenadeRocket C_WeaponGrenadeRocket
+#else
+#include "iscorer.h"
+#endif
+
+class CWeaponGrenadeRocket: public CBaseAnimating
+#if !defined( CLIENT_DLL )
+, public IScorer
+#endif
+{
+ DECLARE_CLASS( CWeaponGrenadeRocket, CBaseAnimating );
+
+public:
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponGrenadeRocket();
+ ~CWeaponGrenadeRocket();
+
+ void Spawn( void );
+ void SetRealOwner( CBasePlayer *pOwner ) { m_hOwner = pOwner; }
+ float GetDamage( void ) { return m_flDamage; }
+ void SetDamage( float flDamage ) { m_flDamage = flDamage; }
+ int GetDamageType() const { return DMG_BLAST; }
+ float GetDamageRadius( void ) { return m_flRadius; }
+ void SetDamageRadius( float flRadius ) { m_flRadius = flRadius; }
+ void SetMaxRange( float flRange );
+ void RocketTouch( CBaseEntity *pOther );
+
+ static CWeaponGrenadeRocket *Create( const Vector &vecOrigin, const Vector &vecAngles, float flMaxRange, CBaseEntity *pRealOwner );
+
+#if !defined( CLIENT_DLL )
+// Server Only
+ void Precache( void );
+
+ void SetAnglesToMatchVelocity( void );
+ void ExceededRangeThink( void );
+ void TrackThink( void );
+ void ApplyForcesToVehicle( CBaseEntity *pEntity );
+
+// IScorer
+public:
+ // Return the entity that should receive the score
+ virtual CBasePlayer *GetScorer( void );
+ // Return the entity that should get assistance credit
+ virtual CBasePlayer *GetAssistant( void ) { return NULL; };
+
+#else
+// Client Only
+ void OnDataChanged( DataUpdateType_t updateType );
+ void ClientThink( void );
+#endif
+
+private:
+
+ CWeaponGrenadeRocket( const CWeaponGrenadeRocket& );
+
+private:
+ float m_flDamage;
+ float m_flRadius;
+ float m_flMaxRange;
+ float m_flFallingSpeed;
+ float m_flExceedRangeTime;
+ EHANDLE m_hOwner;
+};
+
+#endif // WEAPON_GRENADE_ROCKET_H
diff --git a/game/shared/tf2/weapon_harpoon.cpp b/game/shared/tf2/weapon_harpoon.cpp
new file mode 100644
index 0000000..d5da872
--- /dev/null
+++ b/game/shared/tf2/weapon_harpoon.cpp
@@ -0,0 +1,735 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Harpoon
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "basetfcombatweapon_shared.h"
+#include "in_buttons.h"
+#include "engine/IEngineSound.h"
+
+#if defined( CLIENT_DLL )
+#define CWeaponHarpoon C_WeaponHarpoon
+#endif
+
+class CHarpoon;
+
+// Fist defines
+#define FIST_RANGE 90
+
+#if !defined( CLIENT_DLL )
+
+ConVar weapon_harpoon_damage( "weapon_harpoon_damage","40", FCVAR_NONE, "Harpoon impale damage" );
+ConVar weapon_fist_damage( "weapon_fist_damage","50", FCVAR_NONE, "Fist damage to everything other than objects" );
+ConVar weapon_fist_damage_objects( "weapon_fist_damage_objects","150", FCVAR_NONE, "Fist damage to objects" );
+
+#include "rope.h"
+#include "rope_shared.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Harpoon thrown by the harpoon weapon
+//-----------------------------------------------------------------------------
+class CHarpoon : public CBaseAnimating
+{
+ DECLARE_CLASS( CHarpoon, CBaseAnimating );
+public:
+ DECLARE_DATADESC();
+ DECLARE_SERVERCLASS();
+
+ CHarpoon( void );
+ virtual void Spawn( void );
+ virtual void Precache( void );
+
+ void SetHarpoonAngles( void );
+ void FlyThink( void );
+ void ConstrainThink( void );
+ void HarpoonTouch( CBaseEntity *pOther );
+
+ static CHarpoon *Create( const Vector &vecOrigin, const Vector &vecForward, CBasePlayer *pOwner );
+ CRopeKeyframe *GetRope( void ) { return m_hRope; }
+ void SetRope( CRopeKeyframe *pRope ) { m_hRope = pRope; }
+ CBaseEntity *GetImpaledTarget( void ) { return m_hImpaledTarget; }
+ void SetLinkedHarpoon( CHarpoon *pLinkedHarpoon ) { m_hLinkedHarpoon = pLinkedHarpoon; }
+ void CheckLinkedHarpoon( void );
+ void ImpaleTarget( CBaseEntity *pOther );
+
+private:
+ // Impaling
+ CNetworkVector( m_vecOffset );
+ CNetworkQAngle( m_angOffset );
+ float m_flConstrainLength;
+
+ CHandle< CRopeKeyframe > m_hRope;
+ EHANDLE m_hImpaledTarget;
+ CHandle< CHarpoon > m_hLinkedHarpoon;
+};
+
+LINK_ENTITY_TO_CLASS( harpoon, CHarpoon );
+PRECACHE_REGISTER(harpoon);
+
+IMPLEMENT_SERVERCLASS_ST(CHarpoon, DT_Harpoon)
+ SendPropVector( SENDINFO(m_vecOffset), -1, SPROP_COORD ),
+ SendPropVector( SENDINFO(m_angOffset), -1, SPROP_COORD ),
+END_SEND_TABLE()
+
+BEGIN_DATADESC( CHarpoon )
+ // Function Pointers
+ DEFINE_FUNCTION( HarpoonTouch ),
+ DEFINE_FUNCTION( FlyThink ),
+ DEFINE_FUNCTION( ConstrainThink ),
+END_DATADESC()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHarpoon::CHarpoon( void )
+{
+ UseClientSideAnimation();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHarpoon::Spawn( void )
+{
+ Precache();
+
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+ SetSolid( SOLID_BBOX );
+ //m_flGravity = 1.0;
+ SetFriction( 0.75 );
+ SetModel( "models/weapons/w_harpoon.mdl" );
+ UTIL_SetSize(this, Vector( -4, -4, -4), Vector(4, 4, 4));
+ SetCollisionGroup( TFCOLLISION_GROUP_GRENADE );
+
+ SetTouch( HarpoonTouch );
+ SetThink( FlyThink );
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHarpoon::Precache( void )
+{
+ PrecacheModel( "models/weapons/w_harpoon.mdl" );
+
+ PrecacheScriptSound( "Harpoon.Impact" );
+ PrecacheScriptSound( "Harpoon.Impale" );
+ PrecacheScriptSound( "Harpoon.HitFlesh" );
+ PrecacheScriptSound( "Harpoon.HitMetal" );
+ PrecacheScriptSound( "Harpoon.Yank" );
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHarpoon::SetHarpoonAngles( void )
+{
+ QAngle angles;
+ VectorAngles( GetAbsVelocity(), angles );
+ SetLocalAngles( angles );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHarpoon::HarpoonTouch( CBaseEntity *pOther )
+{
+ // If we've stuck something, freeze. Make sure we hit it along our velocity.
+ if ( pOther->GetCollisionGroup() != TFCOLLISION_GROUP_SHIELD )
+ {
+ // Perform the collision response...
+ const trace_t &tr = CBaseEntity::GetTouchTrace( );
+
+ Vector vecNewVelocity;
+ PhysicsClipVelocity (GetAbsVelocity(), tr.plane.normal, vecNewVelocity, 2.0 - GetFriction());
+ SetAbsVelocity( vecNewVelocity );
+ }
+ else
+ {
+ // Move away from the shield...
+ // Fling it out a little extra along the plane normal
+ Vector vecCenter;
+ AngleVectors( pOther->GetAbsAngles(), &vecCenter );
+
+ Vector vecNewVelocity;
+ VectorMultiply( vecCenter, 400.0f, vecNewVelocity );
+ SetAbsVelocity( vecNewVelocity );
+ }
+
+ if ( !pOther->IsBSPModel() && !pOther->GetBaseAnimating() )
+ return;
+
+ // At this point, it shouldn't affect player movement
+ SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+
+ // Remove myself soon
+ SetThink( SUB_Remove );
+ SetNextThink( gpGlobals->curtime + 30.0 );
+
+ m_hImpaledTarget = pOther;
+
+ // Should I impale something?
+ if ( pOther->GetBaseAnimating() )
+ {
+ CheckLinkedHarpoon();
+
+ if ( pOther->GetMoveType() != MOVETYPE_NONE )
+ {
+ ImpaleTarget( pOther );
+ return;
+ }
+ }
+
+ CheckLinkedHarpoon();
+
+ EmitSound( "Harpoon.Impact" );
+
+ // Stop moving
+ SetMoveType( MOVETYPE_NONE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check to see if we've got a linked harpoon, and see if we should constrain something
+//-----------------------------------------------------------------------------
+void CHarpoon::CheckLinkedHarpoon( void )
+{
+ if ( m_hLinkedHarpoon )
+ {
+ CHarpoon *pPlayerHarpoon = NULL;
+ CHarpoon *pNonMovingHarpoon = NULL;
+
+ // Find out if either of us has impaled something
+ if ( GetImpaledTarget() && m_hLinkedHarpoon->GetImpaledTarget() )
+ {
+ // Only care about players for now. One of the targets must be a player.
+ CBaseTFPlayer *pPlayer = NULL;
+ CBaseEntity *pOtherTarget = NULL;
+ if ( GetImpaledTarget()->IsPlayer() )
+ {
+ pPlayer = (CBaseTFPlayer*)GetImpaledTarget();
+ pPlayerHarpoon = this;
+ pNonMovingHarpoon = m_hLinkedHarpoon;
+ }
+ else if ( m_hLinkedHarpoon->GetImpaledTarget()->IsPlayer() )
+ {
+ pPlayer = (CBaseTFPlayer*)m_hLinkedHarpoon->GetImpaledTarget();
+ pNonMovingHarpoon = this;
+ pPlayerHarpoon = m_hLinkedHarpoon;
+ }
+
+ // Found a player?
+ if ( pPlayer )
+ {
+ pOtherTarget = pNonMovingHarpoon->GetImpaledTarget();
+
+ // For now, we have to be linked to a non-moving target. Eventually we could support linked moving targets.
+ // pOtherTarget == NULL means the harpoon's buried in the world.
+ if ( pOtherTarget->IsBSPModel() || pOtherTarget->GetMoveType() == MOVETYPE_NONE )
+ {
+ // Add a little slack
+ m_flConstrainLength = ( m_hLinkedHarpoon->GetAbsOrigin() - GetAbsOrigin() ).Length() + 150;
+ pPlayer->ActivateMovementConstraint( NULL, pNonMovingHarpoon->GetAbsOrigin(), m_flConstrainLength, 150.0f, 0.1f );
+ // Square it for later checking
+ m_flConstrainLength *= m_flConstrainLength;
+
+ // Start checking the length
+ pPlayerHarpoon->m_flConstrainLength = m_flConstrainLength;
+ pPlayerHarpoon->SetThink( ConstrainThink );
+ pPlayerHarpoon->SetNextThink( gpGlobals->curtime + 0.1f );
+
+ // Make the rope taught, and prevent it resizing
+ if ( m_hRope )
+ {
+ m_hRope->m_RopeFlags &= ~ROPE_RESIZE;
+ m_hRope->RecalculateLength();
+ }
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHarpoon::ImpaleTarget( CBaseEntity *pOther )
+{
+ // Impale!
+ EmitSound( "Harpoon.Impale" );
+
+ // Calculate our impale offset
+ m_vecOffset = (pOther->GetAbsOrigin() - GetAbsOrigin());
+ m_angOffset = (pOther->GetAbsAngles() - GetAbsAngles());
+
+ FollowEntity( pOther );
+
+ // Do some damage to the target
+ if ( pOther->m_takedamage )
+ {
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwnerEntity() );
+ if ( !pOwner )
+ return;
+
+ pOther->TakeDamage( CTakeDamageInfo( this, pOwner, weapon_harpoon_damage.GetFloat(), DMG_GENERIC ) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CHarpoon::FlyThink( void )
+{
+ SetHarpoonAngles();
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check to see if our target has moved beyond our length
+//-----------------------------------------------------------------------------
+void CHarpoon::ConstrainThink( void )
+{
+ if ( !GetImpaledTarget() || !m_hLinkedHarpoon.Get() )
+ return;
+
+ // Moved too far away?
+ float flDistSq = m_hLinkedHarpoon->GetAbsOrigin().DistToSqr( GetImpaledTarget()->GetAbsOrigin() );
+ if ( flDistSq > m_flConstrainLength )
+ {
+ // Break the rope
+ if ( m_hRope )
+ {
+ m_hRope->DetachPoint(1);
+ m_hRope->DieAtNextRest();
+ m_hRope = NULL;
+ }
+
+ // If we're impaling a player, remove his movement constraint
+ if ( GetImpaledTarget()->IsPlayer() )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)GetImpaledTarget();
+ pPlayer->DeactivateMovementConstraint();
+ }
+
+ SetThink( NULL );
+ }
+ else
+ {
+ SetNextThink( gpGlobals->curtime + 0.1f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHarpoon *CHarpoon::Create( const Vector &vecOrigin, const Vector &vecForward, CBasePlayer *pOwner )
+{
+ CHarpoon *pHarpoon = (CHarpoon*)CreateEntityByName("harpoon");
+
+ UTIL_SetOrigin( pHarpoon, vecOrigin );
+ pHarpoon->Spawn();
+ pHarpoon->ChangeTeam( pOwner->GetTeamNumber() );
+ pHarpoon->SetOwnerEntity( pOwner );
+ pHarpoon->SetAbsVelocity( vecForward );
+ pHarpoon->SetHarpoonAngles();
+
+ return pHarpoon;
+}
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponHarpoon : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponHarpoon, CBaseTFCombatWeapon );
+public:
+ CWeaponHarpoon();
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual void SecondaryAttack( void );
+ virtual float GetFireRate( void );
+ virtual void ThrowGrenade( void );
+ virtual void DetachRope( void );
+ virtual void YankHarpoon( void );
+
+ // Custom grenade types
+ virtual CHarpoon *CreateHarpoon( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner );
+
+ /*
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+ */
+
+public:
+ CNetworkVar( float, m_flStartedThrowAt );
+ float m_flCantThrowUntil;
+ float m_flSecondaryAttackAt;
+ bool m_bActiveHarpoon;
+
+#if !defined( CLIENT_DLL )
+ CHandle< CRopeKeyframe > m_hRope;
+ CHandle< CHarpoon > m_hHarpoon;
+#endif
+
+private:
+ CWeaponHarpoon( const CWeaponHarpoon & );
+};
+
+LINK_ENTITY_TO_CLASS( weapon_harpoon, CWeaponHarpoon );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponHarpoon, DT_WeaponHarpoon )
+
+BEGIN_NETWORK_TABLE( CWeaponHarpoon, DT_WeaponHarpoon )
+#if !defined( CLIENT_DLL )
+ SendPropTime( SENDINFO( m_flStartedThrowAt ) ),
+#else
+ RecvPropTime( RECVINFO( m_flStartedThrowAt ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponHarpoon )
+
+ DEFINE_PRED_FIELD_TOL( m_flStartedThrowAt, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),
+
+END_PREDICTION_DATA()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponHarpoon::CWeaponHarpoon( void )
+{
+ m_flStartedThrowAt = 0;
+ m_flCantThrowUntil = 0;
+ m_flSecondaryAttackAt = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponHarpoon::GetFireRate( void )
+{
+ return 2.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponHarpoon::ItemPostFrame( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ // Look for button downs
+ if ( (pOwner->m_afButtonPressed & IN_ATTACK) && !m_flStartedThrowAt && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ // If we don't have a harpoon, throw one out. Otherwise, yank it back.
+ if ( m_bActiveHarpoon )
+ {
+ YankHarpoon();
+ }
+ else
+ {
+ m_bActiveHarpoon = true;
+ m_flStartedThrowAt = gpGlobals->curtime;
+ PlayAttackAnimation( ACT_VM_PULLBACK );
+ m_flCantThrowUntil = gpGlobals->curtime + SequenceDuration();
+ }
+ }
+ else if ( m_flCantThrowUntil && m_bActiveHarpoon && !(pOwner->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) && (m_flCantThrowUntil <= gpGlobals->curtime) )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime;
+ PrimaryAttack();
+ m_flStartedThrowAt = 0;
+ m_flCantThrowUntil = 0;
+ }
+ else if ( (pOwner->m_nButtons & IN_ATTACK2) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ PlayAttackAnimation( ACT_VM_SECONDARYATTACK );
+ m_flSecondaryAttackAt = gpGlobals->curtime + SequenceDuration() * 0.3;
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ }
+ else if ( m_flSecondaryAttackAt && m_flSecondaryAttackAt < gpGlobals->curtime )
+ {
+ SecondaryAttack();
+ m_flSecondaryAttackAt = 0;
+ }
+
+ // No buttons down?
+ if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ WeaponIdle( );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponHarpoon::PrimaryAttack( void )
+{
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( !ComputeEMPFireState() )
+ return;
+
+ ThrowGrenade();
+
+ // Setup for refire
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+ CheckRemoveDisguise();
+
+ // If I'm now out of ammo, switch away
+ if ( !HasPrimaryAmmo() )
+ {
+ pPlayer->SelectLastItem();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponHarpoon::SecondaryAttack( void )
+{
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Slap things in front of me
+ Vector vecForward;
+ Vector vecBox = Vector( FIST_RANGE,FIST_RANGE,FIST_RANGE * 1.5 ) * 0.5;
+ pPlayer->EyeVectors( &vecForward );
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecCenter = vecSrc + (FIST_RANGE * 0.5 * vecForward);
+
+#if !defined( CLIENT_DLL )
+ //NDebugOverlay::Box( vecCenter, -Vector(2,2,2), Vector(2,2,2), 255,0,0,20,2.0);
+ //NDebugOverlay::Box( vecCenter, -vecBox, vecBox, 255,255,255,20,2.0);
+
+ bool bHitMetal = false;
+ bool bHitPlayer = false;
+
+ CBaseEntity *pList[100];
+ int count = UTIL_EntitiesInBox( pList, 100, vecSrc - vecBox, vecSrc + vecBox, FL_CLIENT|FL_NPC|FL_OBJECT );
+ for ( int i = 0; i < count; i++ )
+ {
+ CBaseEntity *pEntity = pList[i];
+ if ( !pEntity->m_takedamage )
+ continue;
+ if ( pEntity->InSameTeam( this ) )
+ continue;
+
+ //NDebugOverlay::EntityBounds( pEntity, 0,255,0,20,2.0);
+ if ( pEntity->IsPlayer() )
+ {
+ bHitPlayer = true;
+ CTakeDamageInfo info( this, pPlayer, weapon_fist_damage.GetFloat(), DMG_CLUB );
+ CalculateMeleeDamageForce( &info, (pEntity->GetAbsOrigin() - vecCenter), pEntity->GetAbsOrigin() );
+ pEntity->TakeDamage( info );
+ }
+ else if ( pEntity->Classify() == CLASS_MILITARY )
+ {
+ bHitMetal = true;
+ CTakeDamageInfo info( this, pPlayer, weapon_fist_damage_objects.GetFloat(), DMG_CLUB );
+ CalculateMeleeDamageForce( &info, (pEntity->GetAbsOrigin() - vecCenter), pEntity->GetAbsOrigin() );
+ pEntity->TakeDamage( info );
+ }
+ else
+ {
+ bHitMetal = true;
+ CTakeDamageInfo info( this, pPlayer, weapon_fist_damage.GetFloat(), DMG_CLUB );
+ CalculateMeleeDamageForce( &info, (pEntity->GetAbsOrigin() - vecCenter), pEntity->GetAbsOrigin() );
+ pEntity->TakeDamage( info );
+ }
+ }
+
+ // Play the right sound
+ if ( bHitPlayer )
+ {
+ EmitSound( "Harpoon.HitFlesh" );
+ }
+ else if ( bHitMetal )
+ {
+ EmitSound( "Harpoon.HitMetal" );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponHarpoon::ThrowGrenade( void )
+{
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ BaseClass::WeaponSound(WPN_DOUBLE);
+
+ // Calculate launch velocity (3 seconds for max distance)
+ float flThrowTime = MIN( (gpGlobals->curtime - m_flStartedThrowAt), 3.0 );
+ float flSpeed = 1000 + (200 * flThrowTime);
+
+ PlayAttackAnimation( ACT_VM_PRIMARYATTACK );
+
+ // If the player's crouched, roll the grenade
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ // Launch the grenade
+ Vector vecForward;
+ QAngle vecAngles = pPlayer->EyeAngles();
+ // Throw it up just a tad
+ vecAngles.x = -1;
+ AngleVectors( vecAngles, &vecForward, NULL, NULL);
+ Vector vecOrigin;
+ VectorLerp( pPlayer->EyePosition(), pPlayer->GetAbsOrigin(), 0.25f, vecOrigin );
+ vecOrigin += (vecForward * 16);
+ vecForward = vecForward * flSpeed;
+ CreateHarpoon(vecOrigin, vecForward, pPlayer );
+ }
+ else
+ {
+ // Launch the grenade
+ Vector vecForward;
+ QAngle vecAngles = pPlayer->EyeAngles();
+ AngleVectors( vecAngles, &vecForward, NULL, NULL);
+ Vector vecOrigin = pPlayer->EyePosition();
+ vecOrigin += (vecForward * 16);
+ vecForward = vecForward * flSpeed;
+ CreateHarpoon(vecOrigin, vecForward, pPlayer );
+ }
+
+ pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Give the harpoon a yank
+//-----------------------------------------------------------------------------
+void CWeaponHarpoon::YankHarpoon( void )
+{
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+#if !defined( CLIENT_DLL )
+ if ( m_bActiveHarpoon && m_hHarpoon.Get() )
+ {
+ // If the harpoon's impaled something, pull it towards me
+ CBaseEntity *pTarget = m_hHarpoon->GetImpaledTarget();
+ if ( pTarget )
+ {
+ if ( !pTarget->IsBSPModel() && pTarget->GetMoveType() != MOVETYPE_NONE )
+ {
+ // Bring him to me!
+ EmitSound( "Harpoon.Yank" );
+
+ // Get a yank vector, and raise it a little to get them off the ground if they're on it
+ Vector vecOverHere = ( pPlayer->GetAbsOrigin() - pTarget->GetAbsOrigin() );
+ VectorNormalize( vecOverHere );
+ if ( pTarget->GetFlags() & FL_ONGROUND )
+ {
+ pTarget->SetGroundEntity( NULL );
+ vecOverHere.z = 0.5;
+ }
+ pTarget->ApplyAbsVelocityImpulse( vecOverHere * 500 );
+
+ PlayAttackAnimation( ACT_VM_HAULBACK );
+ }
+ }
+ m_hHarpoon->SetThink( SUB_Remove );
+ m_hHarpoon->SetNextThink( gpGlobals->curtime + 5.0 );
+ m_hHarpoon = NULL;
+ m_bActiveHarpoon = false;
+ }
+
+ DetachRope();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponHarpoon::DetachRope( void )
+{
+#if !defined( CLIENT_DLL )
+ if ( m_hRope )
+ {
+ m_hRope->DetachPoint(1);
+ m_hRope->DieAtNextRest();
+ m_hRope = NULL;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHarpoon *CWeaponHarpoon::CreateHarpoon( const Vector &vecOrigin, const Vector &vecAngles, CBasePlayer *pOwner )
+{
+#if !defined( CLIENT_DLL )
+ CHarpoon *pHarpoon = CHarpoon::Create(vecOrigin, vecAngles, pOwner );
+ if ( pHarpoon )
+ {
+ // Create the rope on first throw. Otherwise attach our existing rope.
+ if ( !m_hRope )
+ {
+ CRopeKeyframe *pRope = CRopeKeyframe::Create( pHarpoon, pOwner, 0, 0 );
+ if ( pRope )
+ {
+ pRope->m_RopeLength = 1.0;
+ pRope->m_Slack = 50.0f;
+ pRope->m_Width = 2;
+ pRope->m_nSegments = ROPE_MAX_SEGMENTS;
+ pRope->m_RopeFlags |= ROPE_RESIZE | ROPE_COLLIDE;
+ }
+ m_hRope = pRope;
+ pHarpoon->SetRope( m_hRope );
+ }
+ else
+ {
+ m_hRope->SetEndPoint( pHarpoon, 0 );
+ pHarpoon->SetRope( m_hRope );
+ m_hRope = NULL;
+ }
+
+ // Do we already have a harpoon out?
+ CHarpoon *pOldHarpoon = m_hHarpoon;
+ m_hHarpoon = pHarpoon;
+
+ if ( pOldHarpoon )
+ {
+ pOldHarpoon->SetLinkedHarpoon( m_hHarpoon );
+ pHarpoon->SetLinkedHarpoon( pOldHarpoon );
+ m_hHarpoon = NULL;
+ }
+ }
+ return pHarpoon;
+#else
+ return NULL;
+#endif
+}
diff --git a/game/shared/tf2/weapon_infiltrator.cpp b/game/shared/tf2/weapon_infiltrator.cpp
new file mode 100644
index 0000000..86dd51b
--- /dev/null
+++ b/game/shared/tf2/weapon_infiltrator.cpp
@@ -0,0 +1,150 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The Infiltrator's Weapon
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "tf_player.h"
+#include "tf_defines.h"
+#include "in_buttons.h"
+#include "weapon_infiltrator.h"
+#include "gamerules.h"
+#include "ammodef.h"
+
+#define INFILTRATOR_STAB_RANGE 64.0f
+
+// Damage CVars
+ConVar weapon_infiltrator_damage( "weapon_infiltrator_damage","0", FCVAR_NONE, "Infiltrator backstab damage" );
+ConVar weapon_infiltrator_range( "weapon_infiltrator_range","0", FCVAR_NONE, "Infiltrator backstab range" );
+
+//=====================================================================================================
+// INFILTRATOR WEAPON
+//=====================================================================================================
+IMPLEMENT_SERVERCLASS_ST(CWeaponInfiltrator, DT_WeaponInfiltrator)
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_infiltrator, CWeaponInfiltrator );
+PRECACHE_WEAPON_REGISTER(weapon_infiltrator);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponInfiltrator::CWeaponInfiltrator( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponInfiltrator::Precache( void )
+{
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponInfiltrator::ComputeEMPFireState( void )
+{
+ if (IsOwnerEMPed())
+ {
+ // FIXME: Need a sound
+ //UTIL_EmitSound( pPlayer->pev, CHAN_WEAPON, g_pszEMPGatlingFizzle, 1.0, ATTN_NORM );
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponInfiltrator::Deploy( void )
+{
+ // Play a shing! sound
+ WeaponSound( SPECIAL1 );
+ return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_VM_DRAW, (char*)GetAnimPrefix() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponInfiltrator::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pTarget = GetAssassinationTarget();
+ if ( pTarget == NULL )
+ return;
+
+ WeaponSound( SINGLE );
+ PlayAttackAnimation( ACT_VM_PRIMARYATTACK );
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration( m_nSequence ) * 0.5;
+
+ // Instant kill
+ pTarget->TakeDamage( CTakeDamageInfo( this, GetOwner(), 500.0f, DMG_SLASH ) );
+ // Gag until respawn
+ pTarget->SetGagged( true );
+
+ CheckRemoveDisguise();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Overloaded to handle the hold-down healing
+//-----------------------------------------------------------------------------
+void CWeaponInfiltrator::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Stab 'em
+ if (( pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ PrimaryAttack();
+ }
+
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: See if there's an assassination target in front of the player.
+// Output : Returns true if a target's found.
+//-----------------------------------------------------------------------------
+CBaseTFPlayer *CWeaponInfiltrator::GetAssassinationTarget( void )
+{
+ if ( !ComputeEMPFireState() )
+ return NULL;
+
+ CBaseTFPlayer *pPlayer = static_cast< CBaseTFPlayer * >( GetOwner() );
+ if ( !pPlayer )
+ return NULL;
+
+ trace_t tr;
+ Vector vecForward;
+ pPlayer->EyeVectors( &vecForward );
+ Vector vecOrigin = pPlayer->EyePosition();
+ UTIL_TraceLine( vecOrigin, vecOrigin + INFILTRATOR_STAB_RANGE * vecForward, MASK_SHOT, pPlayer, GetCollisionGroup(), &tr );
+ if ( tr.fraction == 1.0f || !tr.m_pEnt )
+ return NULL;
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if ( !pEntity || !pEntity->IsPlayer() )
+ return NULL;
+ if ( !pEntity->IsPlayer() )
+ return NULL;
+
+ // Don't target friendlies
+ if ( pEntity->InSameTeam( pPlayer ) )
+ return NULL;
+
+ // See if the player is facing the right direction
+ Vector fwd1, fwd2;
+ AngleVectors( pPlayer->GetAbsAngles(), &fwd1, NULL, NULL );
+ AngleVectors( pEntity->GetAbsAngles(), &fwd2, NULL, NULL );
+
+ float dot = fwd1.Dot( fwd2 );
+ if ( dot <= 0.0f )
+ return NULL;
+
+ return (CBaseTFPlayer*)pEntity;
+} \ No newline at end of file
diff --git a/game/shared/tf2/weapon_infiltrator.h b/game/shared/tf2/weapon_infiltrator.h
new file mode 100644
index 0000000..2dc7060
--- /dev/null
+++ b/game/shared/tf2/weapon_infiltrator.h
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_INFILTRATOR_H
+#define WEAPON_INFILTRATOR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "tf_basecombatweapon.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Infiltrator's weapon
+//-----------------------------------------------------------------------------
+class CWeaponInfiltrator : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponInfiltrator, CBaseTFCombatWeapon );
+public:
+ DECLARE_SERVERCLASS();
+
+ CWeaponInfiltrator();
+
+ virtual void Precache( void );
+ virtual void PrimaryAttack( void );
+ virtual bool ComputeEMPFireState( void );
+ virtual bool Deploy( void );
+ virtual void ItemPostFrame( void );
+
+ CBaseTFPlayer *GetAssassinationTarget( void );
+};
+
+#endif // WEAPON_INFILTRATOR_H
diff --git a/game/shared/tf2/weapon_laserdesignator.cpp b/game/shared/tf2/weapon_laserdesignator.cpp
new file mode 100644
index 0000000..4f9ef46
--- /dev/null
+++ b/game/shared/tf2/weapon_laserdesignator.cpp
@@ -0,0 +1,394 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "NPCEvent.h"
+#include "tf_basecombatweapon.h"
+#include "smoke_trail.h"
+#include "tf_player.h"
+#include "in_buttons.h"
+#include "tf_gamerules.h"
+#include "ammodef.h"
+#include "IEffects.h"
+#include "vstdlib/random.h"
+#include "effects.h"
+#include "baseviewmodel.h"
+#include "basegrenade_shared.h"
+#include "grenade_limpetmine.h"
+#include "tf_obj_sentrygun.h"
+#include "tf_obj_aerial_sentry_station.h"
+
+
+// Damage CVars
+ConVar weapon_laserdesignator_range( "weapon_laserdesignator_range","1024", FCVAR_NONE, "Laser Designator maximum range" );
+
+// ------------------------------------------------------------------------ //
+// CWeaponLaserDesignator
+// ------------------------------------------------------------------------ //
+class CWeaponLaserDesignator : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponLaserDesignator, CBaseTFCombatWeapon );
+public:
+ virtual void Precache( void );
+ virtual float GetFireRate( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ virtual bool ComputeEMPFireState( void );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+
+ bool ValidDesignationTarget( CBaseEntity *pEntity );
+ bool TargetIsInLock( CBaseTFPlayer *pPlayer, CBaseEntity *pTarget, float *flMaxDot );
+
+ // Designation
+ void StartDesignating( void );
+ void StopDesignating( void );
+ void UpdateBeam( void );
+ void DesignateSentriesToAttack( CBaseEntity *pEntity );
+
+public:
+ // Designation
+ bool m_bDesignating;
+
+ // Beam
+ CBeam *m_pBeam;
+};
+
+LINK_ENTITY_TO_CLASS( weapon_laserdesignator, CWeaponLaserDesignator );
+PRECACHE_WEAPON_REGISTER(weapon_laserdesignator);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::Precache( void )
+{
+ BaseClass::Precache();
+ PrecacheModel( "sprites/laserbeam.vmt" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponLaserDesignator::GetFireRate()
+{
+ return 0.2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop thinking and holster
+//-----------------------------------------------------------------------------
+bool CWeaponLaserDesignator::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ StopDesignating();
+ return BaseClass::Holster(pSwitchingTo);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponLaserDesignator::ComputeEMPFireState( void )
+{
+ if (IsOwnerEMPed())
+ {
+ // FIXME: Need a sound
+ //UTIL_EmitSound( pPlayer->pev, CHAN_WEAPON, g_pszEMPGatlingFizzle, 1.0, ATTN_NORM );
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::ItemPostFrame( void )
+{
+ CBasePlayer *pPlayer = GetOwner();
+ if (pPlayer == NULL)
+ return;
+
+ // Are we already welding?
+ if ( m_bDesignating )
+ {
+ if ( pPlayer->m_nButtons & (IN_ATTACK | IN_ATTACK2) )
+ {
+ UpdateBeam();
+ }
+ else
+ {
+ StopDesignating();
+ }
+ }
+ else
+ {
+ if ( (pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ // Start designating
+ PrimaryAttack();
+ UpdateBeam();
+ }
+ }
+
+ // Always idle
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start designating with the laser
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::StartDesignating( void )
+{
+ m_bDesignating = true;
+
+ if ( !m_pBeam )
+ {
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+ int iIndex = pPlayer->entindex();
+
+ m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.vmt", 1 );
+ m_pBeam->PointEntInit( vec3_origin, iIndex );
+ m_pBeam->SetEndAttachment( 1 );
+ m_pBeam->SetColor( 255, 32, 32 );
+ m_pBeam->SetBrightness( 255 );
+ m_pBeam->SetNoise( 0 );
+ m_pBeam->SetWidth( 2 );
+ m_pBeam->SetEndWidth( 1 );
+ m_pBeam->SetStartEntity( ENTINDEX(m_hOwner->edict()) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop designating with the laser
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::StopDesignating( void )
+{
+ m_bDesignating = false;
+ if ( m_pBeam )
+ {
+ UTIL_Remove( m_pBeam );
+ m_pBeam = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+
+ if ( !ComputeEMPFireState() )
+ return;
+
+ WeaponSound(SINGLE);
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+ pPlayer->AddEffects( EF_MUZZLEFLASH );
+
+ StartDesignating();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the beam position and do designator functions
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::UpdateBeam( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+
+ ASSERT( m_pBeam );
+
+ // "Fire" the designator beam
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( pPlayer->GetOrigin() );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+ Vector vecEnd = vecSrc + vecAiming * weapon_laserdesignator_range.GetFloat();
+
+ trace_t tr;
+ TFGameRules()->WeaponTraceLine(vecSrc, vecEnd, MASK_SHOT, pPlayer, DMG_ENERGYBEAM, &tr);
+
+ // Update beam visual
+ m_pBeam->SetStartPos( tr.endpos );
+ m_pBeam->RelinkBeam();
+
+ // Perform designator functions
+ // Did we hit something?
+ CBaseEntity *pEntity = CBaseEntity::Instance( tr.u.ent );
+ if ( pEntity )
+ {
+ // TODO: We dynamic_cast the entity we choose twice. Fix it.
+
+ // If we hit a target we don't care about, try and grab the nearest valid target to our crosshair
+ if ( !ValidDesignationTarget( pEntity ) )
+ {
+ pEntity = NULL;
+
+ // Grab the entity nearest the crosshair
+ float bestdot = AUTOAIM_20DEGREES;
+ float flSize = weapon_laserdesignator_range.GetFloat() * 0.5;
+ Vector vecCenter = vecSrc + vecAiming * flSize;
+
+ // Find a target.
+ CBaseEntity *pList[100];
+ Vector delta( flSize, flSize, flSize );
+ int count = UTIL_EntitiesInBox( pList, 100, vecCenter - delta, vecCenter + delta, FL_CLIENT|FL_NPC|FL_OBJECT );
+ for ( int i = 0; i < count; i++ )
+ {
+ CBaseEntity *pTarget = pList[i];
+ if ( !ValidDesignationTarget(pTarget) )
+ continue;
+ if ( TargetIsInLock( pPlayer, pTarget, &bestdot ) == false )
+ continue;
+ if ( (pTarget->Center() - pPlayer->Center() ).Length() > weapon_laserdesignator_range.GetFloat() )
+ continue;
+
+ // Can shoot at this one
+ pEntity = pTarget;
+ }
+ }
+
+ // Couldn't find anything
+ if ( !pEntity )
+ return;
+
+ // If we hit a player, and it's an enemy, tell my sentryguns to attack it
+ if ( pEntity->IsPlayer() && !pPlayer->InSameTeam(pEntity) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ }
+ else if ( pEntity->GetFlags() & FL_NPC )
+ {
+ // If it's an enemy NPC, tell my sentryguns to attack it
+ if ( !pPlayer->InSameTeam( pEntity ) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ }
+ }
+ else
+ {
+ // Is it a sentrygun? If so, tell it to toggle it's sunken state.
+ if ( pEntity->Classify() == CLASS_MILITARY )
+ {
+ CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun *>(pEntity);
+ if ( pSentry && pSentry->GetBuilder() == pPlayer )
+ {
+ pSentry->ToggleTurtle();
+ }
+ else
+ {
+ // If it's an enemy object, tell my sentryguns to attack it
+ if ( !pPlayer->InSameTeam( pEntity ) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ }
+ }
+ }
+
+ // Is it a limpet mine? If so, detonate it
+ CLimpetMine *pLimpet = dynamic_cast<CLimpetMine *>(pEntity);
+ if ( pLimpet && pLimpet->IsLive() && pLimpet->m_hOwner->pev == pPlayer->pev )
+ {
+ pLimpet->Use( pPlayer, pPlayer, USE_ON, 0 );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the specified entity is a valid one for designation
+//-----------------------------------------------------------------------------
+bool CWeaponLaserDesignator::ValidDesignationTarget( CBaseEntity *pEntity )
+{
+ // Ignore the world
+ if ( pEntity->entindex() == 0 )
+ return false;
+
+ // Ignore players on my team
+ if ( pEntity->IsPlayer() && pEntity->InSameTeam(m_hOwner) )
+ return false;
+
+ // Is it a sentrygun? If so, tell it to toggle it's sunken state.
+ if ( pEntity->Classify() == CLASS_MILITARY )
+ return true;
+
+ // My limpet mines are valid
+ CLimpetMine *pLimpet = dynamic_cast<CLimpetMine *>(pEntity);
+ if ( pLimpet && pLimpet->IsLive() && pLimpet->m_hOwner->pev == m_hOwner->pev )
+ return true;
+
+ // NPCs are valid
+ if ( pEntity->GetFlags() & FL_NPC )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the target entity's close enough to the player's crosshair to lock onto
+//-----------------------------------------------------------------------------
+bool CWeaponLaserDesignator::TargetIsInLock( CBaseTFPlayer *pPlayer, CBaseEntity *pTarget, float *flMaxDot )
+{
+ Vector center = pTarget->Center();
+ Vector dir = center - pPlayer->Center();
+ float length = VectorNormalize( dir );
+ Vector forward, right, up;
+ pPlayer->EyeVectors( &forward, &right, &up );
+
+ // Make sure it's in front of the player
+ if ( DotProduct( dir, forward ) < 0.0f )
+ return false;
+
+ float dot = fabs( DotProduct(dir, right ) ) + fabs( DotProduct(dir, up ) );
+
+ // Tweak for distance
+ dot *= 1.0f + 4.0f * ( length / MAX_COORD_RANGE );
+
+ // Outside the dot?
+ if ( dot > *flMaxDot )
+ return false;
+
+ // Open LOS?
+ trace_t tr;
+ UTIL_TraceLine( pPlayer->Center(), center, MASK_SHOT, edict(), COLLISION_GROUP_NONE, &tr );
+ if ( ( tr.fraction != 1.0f ) && ( tr.u.ent != pTarget->edict() ) )
+ return false;
+
+ *flMaxDot = dot;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the player's sentryguns to all attack the specified target
+//-----------------------------------------------------------------------------
+void CWeaponLaserDesignator::DesignateSentriesToAttack( CBaseEntity *pEntity )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+
+ // Tell all this player's sentryguns
+ for ( int i = 0; i < pPlayer->m_aObjects.Size(); i++ )
+ {
+ CBaseObject *pObj = pPlayer->m_aObjects[i];
+ if ( !pObj )
+ continue;
+
+ if ( pObj->IsSentrygun() )
+ {
+ CObjectSentrygun *pSentry = static_cast<CObjectSentrygun *>(pObj);
+ pSentry->DesignateTarget( pEntity );
+ }
+
+ if ( pObj->GetType() == OBJ_AERIAL_SENTRY_STATION )
+ {
+ CObjectAerialSentryStation *pSentryStation = static_cast<CObjectAerialSentryStation *>(pObj);
+ pSentryStation->DesignateTarget( pEntity );
+ }
+ }
+} \ No newline at end of file
diff --git a/game/shared/tf2/weapon_laserrifle.cpp b/game/shared/tf2/weapon_laserrifle.cpp
new file mode 100644
index 0000000..04a4d5f
--- /dev/null
+++ b/game/shared/tf2/weapon_laserrifle.cpp
@@ -0,0 +1,160 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Medic's Laser rifle
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "NPCEvent.h"
+#include "tf_basecombatweapon.h"
+#include "basecombatcharacter.h"
+#include "smoke_trail.h"
+#include "tf_player.h"
+#include "in_buttons.h"
+#include "tf_gamerules.h"
+#include "ammodef.h"
+#include "IEffects.h"
+#include "vstdlib/random.h"
+
+// Damage CVars
+ConVar weapon_laserrifle_damage( "weapon_laserrifle_damage","0", FCVAR_NONE, "Laser Rifle maximum damage" );
+ConVar weapon_laserrifle_range( "weapon_laserrifle_range","0", FCVAR_NONE, "Laser Rifle maximum range" );
+
+// ------------------------------------------------------------------------ //
+// CWeaponLaserRifle
+// ------------------------------------------------------------------------ //
+class CWeaponLaserRifle : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponLaserRifle, CBaseTFCombatWeapon );
+public:
+ virtual void Precache( void );
+ virtual float GetFireRate( void );
+ virtual void PrimaryAttack( void );
+ virtual bool ComputeEMPFireState( void );
+
+ // Beam
+ int m_iSpriteTexture;
+};
+
+LINK_ENTITY_TO_CLASS( weapon_laserrifle, CWeaponLaserRifle );
+PRECACHE_WEAPON_REGISTER(weapon_laserrifle);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLaserRifle::Precache( void )
+{
+ BaseClass::Precache();
+
+ m_iSpriteTexture = PrecacheModel( "sprites/physbeam.vmt" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponLaserRifle::GetFireRate()
+{
+ return 0.2;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponLaserRifle::ComputeEMPFireState( void )
+{
+ if (IsOwnerEMPed())
+ {
+ // FIXME: Need a sound
+ //UTIL_EmitSound( pPlayer->pev, CHAN_WEAPON, g_pszEMPGatlingFizzle, 1.0, ATTN_NORM );
+ return false;
+ }
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLaserRifle::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( m_hOwner );
+ if ( !pPlayer )
+ return;
+
+ if ( !ComputeEMPFireState() )
+ return;
+
+ WeaponSound(SINGLE);
+
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ pPlayer->AddEffects( EF_MUZZLEFLASH );
+
+ // Fire the beam: BLOW OFF AUTOAIM
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( pPlayer->GetOrigin() );
+ Vector vecAiming, right, up;
+ pPlayer->EyeVectors( &vecAiming, &right, &up);
+
+ Vector vecSpread = VECTOR_CONE_4DEGREES;
+
+ // Get endpoint
+ float x, y, z;
+ do {
+ x = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5);
+ y = random->RandomFloat(-0.5,0.5) + random->RandomFloat(-0.5,0.5);
+ z = x*x+y*y;
+ } while (z > 1);
+ Vector vecDir = vecAiming + x * vecSpread.x * right + y * vecSpread.y * up;
+ Vector vecEnd = vecSrc + vecDir * weapon_laserrifle_range.GetFloat();
+
+ trace_t tr;
+ float damagefactor = TFGameRules()->WeaponTraceLine(vecSrc, vecEnd, MASK_SHOT, pPlayer, DMG_ENERGYBEAM, &tr);
+
+ // Hit target?
+ if (tr.fraction != 1.0)
+ {
+ CBaseEntity *pEntity = CBaseEntity::Instance(tr.u.ent);
+ if ( pEntity )
+ {
+ ClearMultiDamage();
+ float flDamage = GetDamage( (tr.endpos - vecSrc).Length(), tr.hitgroup );
+ flDamage *= damagefactor;
+ pEntity->TraceAttack( CTakeDamageInfo( pPlayer, pPlayer, flDamage, DMG_ENERGYBEAM ), vecDir, &tr );
+ ApplyMultiDamage( pPlayer, pPlayer );
+ }
+
+ g_pEffects->EnergySplash( tr.endpos, tr.plane.normal );
+ }
+
+ // Get hacked gun position
+ AngleVectors( pPlayer->EyeAngles() + pPlayer->m_Local.m_vecPunchAngle, NULL, &right, NULL );
+ Vector vecTracerSrc = vecSrc + Vector (0,0,-8) + right * 12 + vecDir * 16;
+
+ // Laser beam
+ CBroadcastRecipientFilter filter;
+ te->BeamPoints( filter, 0.0,
+ &vecTracerSrc,
+ &tr.endpos,
+ m_iSpriteTexture,
+ 0, // Halo index
+ 0, // Start frame
+ 0, // Frame rate
+ 0.2, // Life
+ 15, // Width
+ 15, // EndWidth
+ 0, // FadeLength
+ 0, // Amplitude
+ 200, // r
+ 200, // g
+ 255, // b
+ 255, // a
+ 255 ); // speed
+
+ pPlayer->m_iAmmo[m_iPrimaryAmmoType]--;
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+
+ CheckRemoveDisguise();
+}
diff --git a/game/shared/tf2/weapon_limpetmine.cpp b/game/shared/tf2/weapon_limpetmine.cpp
new file mode 100644
index 0000000..e85f2b2
--- /dev/null
+++ b/game/shared/tf2/weapon_limpetmine.cpp
@@ -0,0 +1,688 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "in_buttons.h"
+#include "tf_shareddefs.h"
+#include "basegrenade_shared.h"
+#include "basetfcombatweapon_shared.h"
+#include "weapon_limpetmine.h"
+#include "IEffects.h"
+#include "engine/IEngineSound.h"
+#include "weapon_grenade_rocket.h"
+#include "beam_shared.h"
+#include "tf_gamerules.h"
+
+#if defined( CLIENT_DLL )
+#else
+#include "grenade_limpetmine.h"
+#include "tf_obj_sentrygun.h"
+#include "ai_network.h"
+#include "tf_team.h"
+#endif
+
+// Damage CVars
+ConVar weapon_laserdesignator_range( "weapon_laserdesignator_range","2048", FCVAR_REPLICATED, "Laser Designator maximum range" );
+ConVar weapon_limpetmine_max_deployed( "weapon_limpetmine_max_deployed","0", FCVAR_REPLICATED, "Maximum number of Limpet Mines that can be deployed at a single time" );
+ConVar weapon_limpetmine_max_distance_off_trace( "weapon_limpetmine_max_distance_off_trace","25", FCVAR_REPLICATED, "Maximum distance off of the trace that a limpet can be." );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponLimpetmine, DT_WeaponLimpetmine )
+
+BEGIN_NETWORK_TABLE( CWeaponLimpetmine, DT_WeaponLimpetmine )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO( m_iDeployedLimpets ), 5, SPROP_UNSIGNED ),
+ SendPropEHandle( SENDINFO( m_hDesignatedEntity ) ),
+#else
+ RecvPropInt( RECVINFO( m_iDeployedLimpets ) ),
+ RecvPropEHandle( RECVINFO(m_hDesignatedEntity) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponLimpetmine )
+
+ DEFINE_PRED_FIELD( m_iDeployedLimpets, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_hDesignatedEntity, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_limpetmine, CWeaponLimpetmine );
+PRECACHE_WEAPON_REGISTER(weapon_limpetmine);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponLimpetmine::CWeaponLimpetmine( void )
+{
+ SetPredictionEligible( true );
+
+ m_hDesignatedEntity = NULL;
+ m_flNextBuzzTime = 0;
+ m_iDeployedLimpets = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::Precache( void )
+{
+ BaseClass::Precache();
+
+#ifndef CLIENT_DLL
+ UTIL_PrecacheOther( "grenade_limpetmine" );
+#endif
+
+ PrecacheScriptSound( "WeaponLimpetmine.Deny" );
+
+ PrecacheModel( "sprites/laserbeam.vmt" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::UpdateOnRemove( void )
+{
+#ifndef CLIENT_DLL
+ RemoveDeployedLimpets();
+
+ if ( m_hLaserDesignation )
+ {
+ UTIL_Remove( m_hLaserDesignation );
+ }
+#endif
+
+ // Chain at end to mimic destructor unwind order
+ BaseClass::UpdateOnRemove();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::CleanupOnActStart( void )
+{
+#ifndef CLIENT_DLL
+ RemoveDeployedLimpets();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponLimpetmine::GetFireRate( void )
+{
+ return 0.5;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Limpetmine considers itself as having ammo at all times, because it contains the designator
+//-----------------------------------------------------------------------------
+bool CWeaponLimpetmine::HasAnyAmmo( void )
+{
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop thinking and holster
+//-----------------------------------------------------------------------------
+bool CWeaponLimpetmine::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ StopDesignating();
+ return BaseClass::Holster(pSwitchingTo);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::ItemPostFrame( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+#ifndef CLIENT_DLL
+ // If we don't have a laser designator yet, create one
+ if ( !m_hLaserDesignation )
+ {
+ m_hLaserDesignation = CEnvLaserDesignation::Create( pPlayer );
+ }
+#endif
+
+ // Is the player trying to designate?
+ if ( pPlayer->m_nButtons & IN_ATTACK )
+ {
+ if ( m_bDesignating )
+ {
+#ifndef CLIENT_DLL
+ ActivateBeam();
+#endif
+ }
+ else if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ StartDesignating();
+#ifndef CLIENT_DLL
+ ActivateBeam();
+#endif
+ }
+ }
+ else if ( m_bDesignating )
+ {
+ StopDesignating();
+ }
+ else if ( (pPlayer->m_nButtons & IN_ATTACK2) && !m_flStartedLaunchingAt && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ m_flStartedLaunchingAt = gpGlobals->curtime;
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ }
+ else if ( (pPlayer->m_afButtonReleased & IN_ATTACK2) && (m_flNextPrimaryAttack <= gpGlobals->curtime) && m_flStartedLaunchingAt )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime;
+ PrimaryAttack();
+ m_flStartedLaunchingAt = 0;
+ }
+
+ UpdateBeamTarget();
+
+ // Always idle
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Don't allow anymore limpets to be placed if we've reached the max
+ if ( m_iDeployedLimpets >= weapon_limpetmine_max_deployed.GetInt() )
+ {
+#ifdef CLIENT_DLL
+ // We should flash the limpet mine count on the weapon at this point
+ CLocalPlayerFilter filter;
+ EmitSound( filter, entindex(), "WeaponLimpetmine.Deny" );
+ m_flNextBuzzTime = gpGlobals->curtime + 0.5f; // only buzz every so often.
+#endif
+ return;
+ }
+
+ if ( IsOwnerEMPed() )
+ return;
+
+ trace_t tr;
+ // Get an aim vector. Don't use GetAimVector() because we don't want autoaiming.
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+
+ // Calculate launch velocity (3 seconds for max distance)
+ float flThrowTime = MIN( (gpGlobals->curtime - m_flStartedLaunchingAt), 3.0 );
+ float flSpeed = 600 + (300 * flThrowTime);
+ vecAiming *= flSpeed;
+
+ WeaponSound( WPN_DOUBLE );
+#ifndef CLIENT_DLL
+ ThrowLimpet( pPlayer, vecSrc, vecAiming );
+#endif
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_flNextSecondaryAttack = gpGlobals->curtime + GetFireRate();
+
+ pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
+}
+
+#if !defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::ThrowLimpet( CBasePlayer *pPlayer, Vector vecSrc, Vector vecAiming )
+{
+ CLimpetMine *pLimpet = CLimpetMine::Create(vecSrc, vecAiming, pPlayer);
+ pLimpet->SetLauncher( this );
+
+ // Increase the limpet count
+ m_iDeployedLimpets += 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Detonates all deployed limpets for this weapon
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::DetonateDeployedLimpets( void )
+{
+ CBaseEntity *pEntity = NULL;
+ while ((pEntity = gEntList.FindEntityByClassname( pEntity, "grenade_limpetmine" )) != NULL)
+ {
+ CLimpetMine* pLimpet = (CLimpetMine*)pEntity;
+ if ( pLimpet->IsLive() && pLimpet->GetThrower() == GetOwner() )
+ {
+ pLimpet->Use( GetOwner(), this, USE_ON, 0 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Quietly removes all deployed limpets for this weapon
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::RemoveDeployedLimpets( void )
+{
+ CBaseEntity *pEntity = NULL;
+ while ((pEntity = gEntList.FindEntityByClassname( pEntity, "grenade_limpetmine" )) != NULL)
+ {
+ CLimpetMine* pLimpet = (CLimpetMine*)pEntity;
+ if ( pLimpet->GetThrower() == GetOwner() )
+ {
+ pLimpet->Use( GetOwner(), this, USE_SET, 0 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::ActivateBeam( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ Assert( m_pBeam );
+ if( m_hDesignatedEntity != NULL )
+ {
+ CBaseEntity* pEntity = m_hDesignatedEntity.Get();
+
+ // If we hit a player, and it's an enemy, tell my sentryguns to attack it
+ if ( pEntity->IsPlayer() && !pPlayer->InSameTeam(pEntity) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ }
+ else if ( pEntity->GetFlags() & FL_NPC )
+ {
+ // If it's an enemy NPC, tell my sentryguns to attack it
+ if ( !pPlayer->InSameTeam( pEntity ) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ }
+ }
+ else
+ {
+ // Is it a sentrygun? If so, tell it to toggle it's sunken state.
+ if ( pEntity->Classify() == CLASS_MILITARY )
+ {
+ CObjectSentrygun *pSentry = dynamic_cast<CObjectSentrygun *>(pEntity);
+ if ( pSentry && pSentry->GetBuilder() == pPlayer )
+ {
+ pSentry->ToggleTurtle();
+ return;
+ }
+ else
+ {
+ // If it's an enemy object, tell my sentryguns to attack it
+ if ( !pPlayer->InSameTeam( pEntity ) )
+ {
+ DesignateSentriesToAttack( pEntity );
+ return;
+ }
+ }
+ }
+
+ // Is it a limpet mine? If so, detonate it
+ CLimpetMine *pLimpet = dynamic_cast<CLimpetMine *>(pEntity);
+ if ( pLimpet && pLimpet->IsLive() && pLimpet->GetThrower() == pPlayer )
+ {
+ pLimpet->Use( pPlayer, this, USE_ON, 0 );
+ }
+
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the specified entity is a valid one for designation
+//-----------------------------------------------------------------------------
+bool CWeaponLimpetmine::ValidDesignationTarget( CBaseEntity *pEntity )
+{
+ if( !pEntity )
+ return false;
+
+ // Ignore the world
+ if ((!pEntity) || ( pEntity->entindex() == 0 ))
+ return false;
+
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ Assert(pPlayer);
+ if(!pPlayer)
+ return false;
+
+ // Ignore players on my team
+ if ( pEntity->IsPlayer() )
+ {
+ if( pEntity->InSameTeam(GetOwner()) )
+ return false;
+ else
+ return true;
+ }
+
+ // can either target my own sentry guns to turtle, or enemy buildings to target for sentries:
+ if ( pEntity->Classify() == CLASS_MILITARY )
+ {
+ // Is it a sentry gun I built?
+ if ( dynamic_cast<CObjectSentrygun *>(pEntity) && ((CObjectSentrygun*)pEntity)->GetBuilder() == pPlayer )
+ {
+ return true;
+ }
+ // Is it an enemy object?
+ else if ( !pPlayer->InSameTeam( pEntity ) )
+ {
+ return true;
+ }
+ }
+
+ // My limpet mines are valid
+ CLimpetMine *pLimpet = dynamic_cast<CLimpetMine *>(pEntity);
+ if ( pLimpet && pLimpet->IsLive() && pLimpet->GetThrower() == GetOwner() )
+ return true;
+
+ // NPCs are valid
+ if ( pEntity->GetFlags() & FL_NPC )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the player's sentryguns to all attack the specified target
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::DesignateSentriesToAttack( CBaseEntity *pEntity )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Tell all this player's sentryguns
+ for ( int i = 0; i < pPlayer->GetObjectCount(); i++ )
+ {
+ CBaseObject *pObj = pPlayer->GetObject(i);
+ if ( !pObj )
+ continue;
+
+ if ( pObj->IsSentrygun() )
+ {
+ CObjectSentrygun *pSentry = static_cast<CObjectSentrygun *>(pObj);
+ pSentry->DesignateTarget( pEntity );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseEntity* CWeaponLimpetmine::GetDesignatedEntity( CBaseTFPlayer *pPlayer )
+{
+ // Construct the start and end vectors.
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+ Vector vecEnd = vecSrc + vecAiming * weapon_laserdesignator_range.GetFloat();
+
+ float fBestDistanceSq = FLT_MAX;
+ CBaseEntity* pBestEntity = NULL;
+ float fMaxDist = weapon_limpetmine_max_distance_off_trace.GetFloat();
+ float fMaxDistSq = fMaxDist * fMaxDist;
+
+ // Check all limpets against a cylinder:
+ if( CLimpetMine::allLimpets )
+ {
+ for ( CLimpetMine *pLimpet = CLimpetMine::allLimpets ;pLimpet; pLimpet = pLimpet->nextLimpet)
+ {
+ // Find the closest limpet to the center of the cylinder:
+ if( pLimpet->IsLive() && pLimpet->GetThrower() != NULL && pLimpet->GetThrower() == pPlayer )
+ {
+ Vector vecNearestPoint = PointOnLineNearestPoint( vecSrc, vecEnd, pLimpet->GetAbsOrigin() );
+
+ float fDistSq = ( pLimpet->GetAbsOrigin() - vecNearestPoint ).LengthSqr();
+
+ if (( fDistSq > fMaxDistSq ) || ( fDistSq >= fBestDistanceSq ))
+ continue;
+
+ if (TFGameRules()->IsBlockedByEnemyShields( vecSrc, pLimpet->GetAbsOrigin(), GetOwner()->GetTeamNumber() ))
+ continue;
+
+ pBestEntity = pLimpet;
+ fBestDistanceSq = fDistSq;
+ }
+ }
+
+ if( pBestEntity )
+ return pBestEntity;
+ }
+
+ // Check to see if we are designating a combat object:
+ for ( int i = 0; i < pPlayer->GetObjectCount(); i++ )
+ {
+ CBaseObject *pObj = pPlayer->GetObject(i);
+ if ( !pObj || !pObj->IsSentrygun() )
+ continue;
+
+ Vector vecObjCenter = pObj->WorldSpaceCenter();
+ Vector vecNearestPoint = PointOnLineNearestPoint( vecSrc, vecEnd, vecObjCenter );
+
+ float fDistSq = ( vecObjCenter - vecNearestPoint ).LengthSqr();
+
+ if (( fDistSq > fMaxDistSq ) || ( fDistSq >= fBestDistanceSq ))
+ continue;
+
+ if (TFGameRules()->IsBlockedByEnemyShields( vecSrc, vecObjCenter, GetOwner()->GetTeamNumber() ))
+ continue;
+
+ pBestEntity = pObj;
+ fBestDistanceSq = fDistSq;
+ }
+
+ // Valid or NULL
+ return pBestEntity;
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::WeaponIdle( void )
+{
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ if ( m_bDesignating || m_flStartedLaunchingAt )
+ {
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ }
+ else
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ SetWeaponIdleTime( gpGlobals->curtime + 0.2 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Start designating with the laser
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::StartDesignating( void )
+{
+ m_bDesignating = true;
+
+ // Add myself to the designated target list
+ Vector vecOrigin(0,0,0);
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+#ifndef CLIENT_DLL
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( !m_pBeam )
+ {
+ m_pBeam = CBeam::BeamCreate( "sprites/laserbeam.vmt", 5 );
+
+ m_pBeam->PointEntInit( vec3_origin, this );
+ m_pBeam->SetEndAttachment( 1 );
+ m_pBeam->SetColor( 255, 32, 32 );
+ m_pBeam->SetBrightness( 255 );
+ m_pBeam->SetNoise( 0 );
+ m_pBeam->SetWidth( 0.5 );
+ m_pBeam->SetEndWidth( 0.5 );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop designating with the laser
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::StopDesignating( void )
+{
+ SendWeaponAnim( ACT_VM_IDLE );
+
+ m_bDesignating = false;
+
+#ifndef CLIENT_DLL
+ if ( m_pBeam )
+ {
+ UTIL_Remove( m_pBeam );
+ m_pBeam = NULL;
+ }
+
+ if ( m_hLaserDesignation )
+ {
+ m_hLaserDesignation->SetActive( false );
+ }
+
+ // Remove any designated target:
+ m_hDesignatedEntity.Set( NULL );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update the beam position and do designator functions
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::UpdateBeamTarget()
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // "Fire" the designator beam
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+ Vector vecEnd = vecSrc + vecAiming * weapon_laserdesignator_range.GetFloat();
+
+ trace_t tr;
+ TFGameRules()->WeaponTraceLine(vecSrc, vecEnd, MASK_SHOT, pPlayer, DMG_PROBE, &tr);
+
+ // Only update our designated target point if we hit something
+#ifndef CLIENT_DLL
+ if ( tr.fraction != 1.0 && m_bDesignating )
+ {
+ m_hLaserDesignation->SetActive( true );
+ m_hLaserDesignation->SetAbsOrigin( tr.endpos );
+ }
+ else
+ {
+ m_hLaserDesignation->SetActive( false );
+ }
+
+ // Update beam visual
+ if( m_pBeam )
+ {
+ m_pBeam->SetStartPos( tr.endpos );
+ m_pBeam->RelinkBeam();
+ }
+
+ CBaseEntity* pEntity= NULL;
+
+ // Perform designator functions
+ // Did we hit something?
+ //pEntity = CBaseEntity::Instance( tr.u.ent );
+
+ // If we hit a target we don't care about, try and grab the nearest valid target to our crosshair
+ //if ( !ValidDesignationTarget( pEntity ) )
+ {
+ // Get the designated entity, ignoring trace information:
+ pEntity = GetDesignatedEntity( pPlayer );
+
+ if( !ValidDesignationTarget( pEntity ) )
+ {
+ m_hDesignatedEntity.Set( NULL );
+ return;
+ }
+ }
+
+ // We have a valid entity, light it up.
+ m_hDesignatedEntity.Set( pEntity );
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponLimpetmine::DecrementLimpets( void )
+{
+#ifndef CLIENT_DLL
+ if ( m_iDeployedLimpets )
+ {
+ m_iDeployedLimpets -= 1;
+ }
+#endif
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose: Limpet mine's always selectable, because the laser designator can always fire
+//-----------------------------------------------------------------------------
+bool C_WeaponLimpetmine::CanBeSelected( void )
+{
+ return true;
+}
+
+void C_WeaponLimpetmine::PreDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PreDataUpdate( updateType );
+
+ m_hOldDesignatedEntity = m_hDesignatedEntity;
+}
+
+void C_WeaponLimpetmine::PostDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PostDataUpdate( updateType );
+
+ if ( GetOwner() == C_BaseTFPlayer::GetLocalPlayer() )
+ {
+ // Old target isn't valid anymore, so reset it's render properties
+ if ( m_hOldDesignatedEntity.Get() && m_hOldDesignatedEntity != m_hDesignatedEntity )
+ {
+ m_hOldDesignatedEntity->m_nRenderFX = m_eDesignatedEntityOriginalRenderFx;
+ m_hOldDesignatedEntity->SetRenderMode( (RenderMode_t)m_eDesignatedEntityOriginalRenderMode );
+ }
+
+ // New target? Set it's render properties
+ if ( m_hDesignatedEntity != NULL && m_hOldDesignatedEntity != m_hDesignatedEntity )
+ {
+ // Store off original info for designated entity
+ m_eDesignatedEntityOriginalRenderFx = m_hDesignatedEntity->m_nRenderFX;
+ m_eDesignatedEntityOriginalRenderMode = m_hDesignatedEntity->GetRenderMode();
+
+ // Set up alpha blended pulsefast renderer
+ m_hDesignatedEntity->m_nRenderFX = kRenderFxPulseFastWider;
+ m_hDesignatedEntity->SetRenderMode( kRenderTransAlpha );
+ }
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/game/shared/tf2/weapon_limpetmine.h b/game/shared/tf2/weapon_limpetmine.h
new file mode 100644
index 0000000..3040e14
--- /dev/null
+++ b/game/shared/tf2/weapon_limpetmine.h
@@ -0,0 +1,109 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_LIMPETMINE_H
+#define WEAPON_LIMPETMINE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CBeam;
+
+#if defined( CLIENT_DLL )
+#define CWeaponLimpetmine C_WeaponLimpetmine
+#else
+#include "env_laserdesignation.h"
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: Combination limpet mine & laser designator weapon
+//-----------------------------------------------------------------------------
+class CWeaponLimpetmine : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponLimpetmine, CBaseTFCombatWeapon );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponLimpetmine( void );
+
+ virtual void UpdateOnRemove( void );
+
+ virtual void Precache( void );
+ virtual float GetFireRate( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual void WeaponIdle( void );
+ virtual bool HasAnyAmmo( void );
+ virtual void CleanupOnActStart( void );
+
+ // Designation
+ void StartDesignating( void );
+ void StopDesignating( void );
+ void UpdateBeamTarget( );
+
+ // Limpet counting
+ void DecrementLimpets( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+// Server DLL only
+#if !defined( CLIENT_DLL )
+ void DetonateDeployedLimpets( void );
+ void RemoveDeployedLimpets( void );
+ void ThrowLimpet( CBasePlayer *pPlayer, Vector vecSrc, Vector vecAiming );
+ void ActivateBeam();
+ void DesignateSentriesToAttack( CBaseEntity *pEntity );
+ bool ValidDesignationTarget( CBaseEntity *pEntity );
+ CBaseEntity *GetDesignatedEntity( CBaseTFPlayer *pPlayer ); // Visually highlight those limpets in player's view cone
+
+public:
+ CHandle<CEnvLaserDesignation> m_hLaserDesignation;
+#endif
+
+// Client DLL only
+#if defined( CLIENT_DLL )
+public:
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ virtual bool CanBeSelected( void );
+ virtual void PreDataUpdate( DataUpdateType_t updateType );
+ virtual void PostDataUpdate( DataUpdateType_t updateType );
+#endif
+
+protected:
+ // Designation
+ bool m_bDesignating;
+ CNetworkHandle( CBaseEntity, m_hDesignatedEntity );
+ EHANDLE m_hOldDesignatedEntity;
+ int m_eDesignatedEntityOriginalRenderFx;
+ int m_eDesignatedEntityOriginalRenderMode;
+
+ // Beam
+ CBeam *m_pBeam;
+
+ // Limpets
+ float m_flStartedLaunchingAt;
+ CNetworkVar( int, m_iDeployedLimpets );
+ int m_flNextBuzzTime;
+
+private:
+ CWeaponLimpetmine( const CWeaponLimpetmine & );
+};
+
+#endif // WEAPON_LIMPETMINE_H
diff --git a/game/shared/tf2/weapon_minigun.cpp b/game/shared/tf2/weapon_minigun.cpp
new file mode 100644
index 0000000..a8fcb3e
--- /dev/null
+++ b/game/shared/tf2/weapon_minigun.cpp
@@ -0,0 +1,415 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Minigun
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "in_buttons.h"
+#include "takedamageinfo.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "tf_gamerules.h"
+
+// Damage CVars
+ConVar weapon_minigun_damage( "weapon_minigun_damage","10", FCVAR_REPLICATED, "Minigun damage per pellet" );
+ConVar weapon_minigun_range( "weapon_minigun_range","1500", FCVAR_REPLICATED, "Minigun maximum range" );
+ConVar weapon_minigun_pellets( "weapon_minigun_pellets","2", FCVAR_REPLICATED, "Minigun pellets per fire" );
+ConVar weapon_minigun_ducking_mod( "weapon_minigun_ducking_mod", "0.75", FCVAR_REPLICATED, "Minigun ducking speed modifier" );
+
+#if defined( CLIENT_DLL )
+#include "hud.h"
+#include "fx.h"
+#define CWeaponMinigun C_WeaponMinigun
+extern ConVar zoom_sensitivity_ratio;
+extern ConVar default_fov;
+#else
+#endif
+
+// Time taken to fully wind up/down
+#define MINIGUN_WIND_TIME 2
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponMinigun : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponMinigun, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponMinigun( void );
+
+ virtual void Precache();
+
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual const Vector& GetBulletSpread( void );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual void AddViewKick( void );
+ virtual float GetFireRate( void );
+ virtual float GetDefaultAnimSpeed( void );
+ virtual void BulletWasFired( const Vector &vecStart, const Vector &vecEnd );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+ void ReduceRotation( void );
+ void AttemptToReload( void );
+
+#if defined( CLIENT_DLL )
+public:
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() &&
+ GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+ virtual void ClientThink( void );
+#endif
+
+public:
+ float m_flOwnersMaxSpeed;
+ CNetworkVar( float, m_flRotationSpeed ); // When 1, firing commences.
+ bool m_bSoundPlaying;
+
+private:
+ CWeaponMinigun( const CWeaponMinigun & );
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponMinigun::CWeaponMinigun( void )
+{
+ SetPredictionEligible( true );
+ m_flRotationSpeed = 0;
+ m_bSoundPlaying = false;
+}
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponMinigun, DT_WeaponMinigun )
+
+BEGIN_NETWORK_TABLE( CWeaponMinigun, DT_WeaponMinigun )
+#if !defined( CLIENT_DLL )
+ SendPropFloat( SENDINFO( m_flRotationSpeed ), 8, SPROP_ROUNDDOWN, 0, 1 ),
+#else
+ RecvPropFloat( RECVINFO( m_flRotationSpeed ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponMinigun )
+ DEFINE_PRED_FIELD_TOL( m_flRotationSpeed, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.01f ),
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_minigun, CWeaponMinigun );
+PRECACHE_WEAPON_REGISTER(weapon_minigun);
+
+void CWeaponMinigun::Precache()
+{
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponMinigun::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ m_flRotationSpeed = 0;
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the accuracy derived from weapon and player, and return it
+//-----------------------------------------------------------------------------
+const Vector& CWeaponMinigun::GetBulletSpread( void )
+{
+ static Vector cone = VECTOR_CONE_8DEGREES;
+ return cone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMinigun::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ // This should work, and avoids sending extra network data. If it doesn't, we'll have to send down the unchanged max speed.
+ if ( !m_flRotationSpeed )
+ {
+ m_flOwnersMaxSpeed = pOwner->MaxSpeed();
+ }
+
+ float flLastRotationSpeed = m_flRotationSpeed;
+
+ CheckReload();
+
+ // Handle firing
+ if ( (pOwner->m_nButtons & IN_ATTACK ) && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ if ( m_iClip1 > 0 )
+ {
+ if ( m_flRotationSpeed < 0.99 )
+ {
+ // If we're starting, play the sound
+ m_flRotationSpeed = min(1, m_flRotationSpeed + (gpGlobals->frametime / MINIGUN_WIND_TIME) );
+ }
+ else
+ {
+ PrimaryAttack();
+ }
+ }
+ else
+ {
+ AttemptToReload();
+ }
+ }
+
+ // Reload button (or fire button when we're out of ammo)
+ if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ if ( pOwner->m_nButtons & IN_RELOAD )
+ {
+ AttemptToReload();
+ }
+ else if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ if ( !m_iClip1 && HasPrimaryAmmo() )
+ {
+ AttemptToReload();
+ }
+ else
+ {
+ ReduceRotation();
+ }
+ }
+ }
+
+ // If the speed changed, modify our movement speed
+ if ( m_flRotationSpeed != flLastRotationSpeed )
+ {
+ pOwner->SetMaxSpeed( m_flOwnersMaxSpeed * (1.0 - (m_flRotationSpeed * 0.5)) );
+ }
+
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMinigun::ReduceRotation( void )
+{
+ if ( m_flRotationSpeed > 0 )
+ {
+ m_flRotationSpeed = MAX(0, m_flRotationSpeed - (gpGlobals->frametime / MINIGUN_WIND_TIME) );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMinigun::AttemptToReload( void )
+{
+ // Wind down before reloading
+ if ( m_flRotationSpeed > 0 )
+ {
+ ReduceRotation();
+ }
+ else
+ {
+ Reload();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMinigun::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ // Fire the bullets
+ Vector vecSrc = pPlayer->Weapon_ShootPosition( );
+ Vector vecAiming;
+ pPlayer->EyeVectors( &vecAiming );
+
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ // Make a satisfying force, and knock them into the air
+ float flForceScale = (100) * 75 * 4;
+ Vector vecForce = vecAiming;
+ vecForce.z += 0.7;
+ vecForce *= flForceScale;
+
+ CTakeDamageInfo info( this, pPlayer, vecForce, vec3_origin, weapon_minigun_damage.GetFloat(), DMG_BULLET | DMG_BUCKSHOT);
+ TFGameRules()->FireBullets( info, weapon_minigun_pellets.GetFloat(),
+ vecSrc, vecAiming, GetBulletSpread(), weapon_minigun_range.GetFloat(), m_iPrimaryAmmoType, 0, entindex(), 0 );
+
+ AddViewKick();
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_iClip1 = m_iClip1 - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMinigun::AddViewKick( void )
+{
+ // Get the view kick
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ QAngle viewPunch;
+ viewPunch.x = SHARED_RANDOMFLOAT( -0.5f, 0.5f );
+ viewPunch.y = SHARED_RANDOMFLOAT( -1.0f, 1.0f );
+ viewPunch.z = 0;
+
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ viewPunch *= 0.25;
+ }
+
+ pPlayer->ViewPunch( viewPunch );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponMinigun::GetFireRate( void )
+{
+ float flFireRate = SHARED_RANDOMFLOAT( 0.05, 0.1 );
+
+ CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( GetOwner() );
+ if ( pPlayer )
+ {
+ // Ducking players should fire more rapidly.
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ flFireRate *= weapon_minigun_ducking_mod.GetFloat();
+ }
+ }
+
+ return flFireRate;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Match the anim speed to the weapon speed while crouching
+//-----------------------------------------------------------------------------
+float CWeaponMinigun::GetDefaultAnimSpeed( void )
+{
+ if ( GetOwner() && GetOwner()->IsPlayer() )
+ {
+ if ( GetOwner()->GetFlags() & FL_DUCKING )
+ return (1.0 + (1.0 - weapon_minigun_ducking_mod.GetFloat()) );
+ }
+
+ return 1.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Draw the minigun effect
+//-----------------------------------------------------------------------------
+void CWeaponMinigun::BulletWasFired( const Vector &vecStart, const Vector &vecEnd )
+{
+ UTIL_Tracer( (Vector&)vecStart, (Vector&)vecEnd, entindex(), 1, 5000, false, "MinigunTracer" );
+}
+
+#if defined ( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponMinigun::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ // Suppress the shell ejection from the HL2 model we're using for prototyping
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void CWeaponMinigun::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMinigun::ClientThink()
+{
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetOwner();
+ if (!pPlayer)
+ return;
+
+ if ( m_flRotationSpeed )
+ {
+ WeaponSound_t nSound = SPECIAL1;
+
+ // If we're firing, play that sound instead
+ if ( m_flRotationSpeed >= 0.99 )
+ {
+ nSound = SINGLE;
+ }
+ else
+ {
+ m_bSoundPlaying = true;
+ }
+
+ // If we have some sounds from the weapon classname.txt file, play a random one of them
+ const char *shootsound = GetShootSound( nSound );
+ if ( !shootsound || !shootsound[0] )
+ return;
+
+ CSoundParameters params;
+ if ( !GetParametersForSound( shootsound, params, NULL ) )
+ return;
+
+ // Shift pitch according to barrel rotation
+ float flPitch = 30 + (90 * m_flRotationSpeed);
+
+ CPASAttenuationFilter filter( GetOwner(), params.soundlevel );
+ Vector vecOrigin = GetOwner()->GetAbsOrigin();
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_WEAPON;
+ ep.m_pSoundName = shootsound;
+ ep.m_flVolume = params.volume;
+ ep.m_SoundLevel = params.soundlevel;
+ ep.m_nFlags = SND_CHANGE_PITCH;
+ ep.m_nPitch = (int)flPitch;
+ ep.m_pOrigin = &vecOrigin;
+
+
+ EmitSound( filter, GetOwner()->entindex(), ep );
+ }
+ else if ( m_bSoundPlaying )
+ {
+ m_bSoundPlaying = false;
+ StopWeaponSound( SPECIAL1 );
+ StopWeaponSound( SINGLE );
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/game/shared/tf2/weapon_mortar.cpp b/game/shared/tf2/weapon_mortar.cpp
new file mode 100644
index 0000000..2fcd467
--- /dev/null
+++ b/game/shared/tf2/weapon_mortar.cpp
@@ -0,0 +1,367 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "tf_basecombatweapon.h"
+#include "tf_defines.h"
+#include "in_buttons.h"
+#include "gamerules.h"
+#include "ammodef.h"
+#include "tf_obj.h"
+#include "weapon_mortar.h"
+#include "smoke_trail.h"
+#include "tf_shareddefs.h"
+#include "tf_team.h"
+#include "tf_gamerules.h"
+#include "tf_obj_antimortar.h"
+#include "vstdlib/random.h"
+#include "engine/IEngineSound.h"
+
+extern short g_sModelIndexFireball;
+
+// Damage CVars
+ConVar weapon_mortar_shell_damage( "weapon_mortar_shell_damage","0", FCVAR_NONE, "Mortar's standard shell maximum damage" );
+ConVar weapon_mortar_shell_radius( "weapon_mortar_shell_radius","0", FCVAR_NONE, "Mortar's standard shell splash radius" );
+ConVar weapon_mortar_starburst_damage( "weapon_mortar_starburst_damage","0", FCVAR_NONE, "Mortar's starburst maximum damage" );
+ConVar weapon_mortar_starburst_radius( "weapon_mortar_starburst_radius","0", FCVAR_NONE, "Mortar's starburst splash radius" );
+ConVar weapon_mortar_cluster_shells( "weapon_mortar_cluster_shells","0", FCVAR_NONE, "Number of shells a mortar cluster round bursts into" );
+
+
+//=====================================================================================================
+// MORTAR WEAPON
+//=====================================================================================================
+LINK_ENTITY_TO_CLASS( weapon_mortar, CWeaponMortar );
+PRECACHE_WEAPON_REGISTER(weapon_mortar);
+
+EXTERN_SEND_TABLE(DT_BaseCombatWeapon)
+
+IMPLEMENT_SERVERCLASS_ST(CWeaponMortar, DT_WeaponMortar)
+ SendPropInt( SENDINFO( m_bCarried ), 2, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_bMortarReloading ), 2, SPROP_UNSIGNED ),
+ SendPropVector( SENDINFO(m_vecMortarOrigin), -1, SPROP_COORD ),
+ SendPropVector( SENDINFO(m_vecMortarAngles), -1, SPROP_COORD ),
+END_SEND_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponMortar::CWeaponMortar( void )
+{
+#ifdef _DEBUG
+ m_vecMortarOrigin.Init();
+ m_vecMortarAngles.Init();
+#endif
+
+ m_bCarried = true;
+ m_bMortarReloading = false;
+ m_hDeployedMortar = NULL;
+ m_bRangeUpgraded = false;
+ m_bAccuracyUpgraded = false;
+}
+
+void CWeaponMortar::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "WeaponMortar.EMPed" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponMortar::GetFireRate( void )
+{
+ return 3.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponMortar::Deploy( )
+{
+ return DefaultDeploy( (char*)GetViewModel(), (char*)GetWorldModel(), ACT_SLAM_TRIPMINE_DRAW, (char*)GetAnimPrefix() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMortar::ItemPostFrame( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if (pPlayer == NULL)
+ return;
+
+ if ( pPlayer->m_nButtons & IN_ATTACK )
+ {
+ if ( m_bCarried )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "\n\n\n\n\n\n\n\n\n\nBuild your mortar with the build weapon first!" );
+ }
+ }
+ else if ( pPlayer->m_nButtons & IN_ATTACK2 )
+ {
+ SecondaryAttack();
+ }
+
+ // No buttons down.
+ if (!(( pPlayer->m_nButtons & IN_ATTACK ) || ( pPlayer->m_nButtons & IN_ATTACK2 )))
+ {
+ WeaponIdle();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponMortar::ComputeEMPFireState( void )
+{
+ if (IsOwnerEMPed())
+ {
+ CPASAttenuationFilter filter( GetOwner(), "WeaponMortar.EMPed" );
+ EmitSound( filter, GetOwner()->entindex(), "WeaponMortar.EMPed" );
+ return false;
+ }
+ return true;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMortar::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+ // Can't attack if taking EMP damage
+ if ( !ComputeEMPFireState() )
+ return;
+ if ( IsOwnerEMPed() )
+ return;
+ if ( m_hDeployedMortar == NULL )
+ return;
+
+ if ( m_hDeployedMortar->FireMortar( m_flFiringPower, m_flFiringAccuracy, m_bRangeUpgraded, m_bAccuracyUpgraded ) )
+ {
+ WeaponSound( SINGLE );
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
+
+ CheckRemoveDisguise();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMortar::Fire( float flPower, float flAccuracy )
+{
+ m_flFiringPower = flPower;
+ m_flFiringAccuracy = flAccuracy;
+
+ PrimaryAttack();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMortar::SecondaryAttack( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Setup for refire
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 1.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Player's finished deploying his mortar
+//-----------------------------------------------------------------------------
+void CWeaponMortar::DeployMortar( CObjectMortar *pMortar )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "\n\n\n\n\n\n\n\n\n\nMortar Deployed" );
+
+ m_bCarried = false;
+ m_hDeployedMortar = pMortar;
+ m_hDeployedMortar->m_hMortarWeapon = this;
+ SendWeaponAnim( ACT_SLAM_DETONATOR_DRAW );
+
+ m_vecMortarOrigin = m_hDeployedMortar->GetLocalOrigin();
+ m_vecMortarAngles = m_hDeployedMortar->GetLocalAngles();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Mortar object has been removed
+//-----------------------------------------------------------------------------
+void CWeaponMortar::MortarObjectRemoved( void )
+{
+ m_hDeployedMortar = NULL;
+ m_bCarried = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMortar::SetYaw( float flYaw )
+{
+ if ( m_hDeployedMortar == NULL )
+ return;
+
+ QAngle angles = m_hDeployedMortar->GetLocalAngles();
+ angles.y = flYaw;
+ m_hDeployedMortar->SetLocalAngles( angles );
+ m_vecMortarAngles = angles;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the deployed mortar's firing round
+//-----------------------------------------------------------------------------
+void CWeaponMortar::SetRoundType( int iRoundType )
+{
+ if ( m_hDeployedMortar == NULL )
+ return;
+
+ // Make sure we've got the technology for this round type
+ if ( MortarAmmoTechs[ iRoundType ] && MortarAmmoTechs[ iRoundType ][0] )
+ {
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+ // Does the player have the technology?
+ if ( pPlayer->HasNamedTechnology( MortarAmmoTechs[ iRoundType ] ) == false )
+ return;
+ }
+
+ m_hDeployedMortar->m_iRoundType = iRoundType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMortar::MortarDestroyed( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "\n\n\n\n\n\n\n\n\n\nMortar Destroyed!" );
+ }
+
+ if ( pPlayer && pPlayer->GetActiveWeapon() == this )
+ {
+ SendWeaponAnim( ACT_SLAM_TRIPMINE_DRAW );
+ }
+
+ MortarObjectRemoved();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObject -
+//-----------------------------------------------------------------------------
+void CWeaponMortar::AddAssociatedObject( CBaseObject *pObject )
+{
+ Assert( pObject );
+
+ // Can't handle this object
+ CObjectMortar *mortar = dynamic_cast< CObjectMortar * >( pObject );
+ if ( !mortar )
+ return;
+
+ m_bCarried = false;
+
+ m_hDeployedMortar = mortar;
+ mortar->m_hMortarWeapon = this;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pObject -
+//-----------------------------------------------------------------------------
+void CWeaponMortar::RemoveAssociatedObject( CBaseObject *pObject )
+{
+ Assert( pObject );
+
+ // Can't handle this object
+ CObjectMortar *mortar = dynamic_cast< CObjectMortar * >( pObject );
+ if ( !mortar )
+ return;
+
+ if ( m_hDeployedMortar == mortar )
+ {
+ m_hDeployedMortar = NULL;
+ m_bCarried = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The player holding this weapon has just gained new technology.
+// Check to see if it affects the mortar
+//-----------------------------------------------------------------------------
+void CWeaponMortar::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( pPlayer )
+ {
+ // Range upgraded?
+ if ( pPlayer->HasNamedTechnology("mortar_range") )
+ m_bRangeUpgraded = true;
+ else
+ m_bRangeUpgraded = false;
+
+ // Accuracy upgraded?
+ if ( pPlayer->HasNamedTechnology("mortar_accuracy") )
+ m_bAccuracyUpgraded = true;
+ else
+ m_bAccuracyUpgraded = false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponMortar::WeaponIdle( void )
+{
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ if ( m_bCarried )
+ {
+ SendWeaponAnim( ACT_SLAM_TRIPMINE_IDLE );
+ }
+ else
+ {
+ SendWeaponAnim( ACT_SLAM_DETONATOR_IDLE );
+ }
+
+ SetWeaponIdleTime( gpGlobals->curtime + 1.0 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: My mortar object is reloading
+//-----------------------------------------------------------------------------
+void CWeaponMortar::MortarIsReloading( void )
+{
+ m_bMortarReloading = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: My mortar object has finished reloading
+//-----------------------------------------------------------------------------
+void CWeaponMortar::MortarFinishedReloading( void )
+{
+ m_bMortarReloading = false;
+}
diff --git a/game/shared/tf2/weapon_mortar.h b/game/shared/tf2/weapon_mortar.h
new file mode 100644
index 0000000..c32db7d
--- /dev/null
+++ b/game/shared/tf2/weapon_mortar.h
@@ -0,0 +1,84 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_MORTAR_H
+#define WEAPON_MORTAR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class SmokeTrail;
+class CWeaponMortar;
+
+#include "tf_basecombatweapon.h"
+#include "particle_smokegrenade.h"
+#include "utllinkedlist.h"
+#include "tf_obj_mortar.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponMortar : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponMortar, CBaseTFCombatWeapon );
+public:
+ DECLARE_SERVERCLASS();
+
+ CWeaponMortar();
+
+ virtual void Precache();
+
+ // Input
+ void Fire( float flPower, float flAccuracy );
+ void PrimaryAttack( void );
+ void SecondaryAttack( void );
+
+ // Deployment / Pick-up
+ void DeployMortar( CObjectMortar *pMortar );
+ void PickupMortar( void );
+ void MortarDestroyed( void );
+ void MortarObjectRemoved( void );
+
+ // Turning
+ void SetYaw( float flYaw );
+
+ // Firing
+ virtual void ItemPostFrame( void );
+ virtual float GetFireRate( void );
+
+ // Ammo
+ virtual void SetRoundType( int iRoundType );
+
+ virtual bool Deploy( void );
+ virtual void WeaponIdle( void );
+
+ virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
+ virtual void AddAssociatedObject( CBaseObject *pObject );
+ virtual void RemoveAssociatedObject( CBaseObject *pObject );
+
+ virtual bool ComputeEMPFireState( void );
+
+ void MortarIsReloading( void );
+ void MortarFinishedReloading( void );
+
+public:
+ // Data
+ bool m_bCarried;
+ bool m_bMortarReloading;
+ CHandle< CObjectMortar > m_hDeployedMortar;
+ Vector m_vecMortarOrigin;
+ QAngle m_vecMortarAngles;
+ bool m_bRangeUpgraded;
+ bool m_bAccuracyUpgraded;
+
+ // Firing
+ float m_flFiringPower;
+ float m_flFiringAccuracy;
+};
+
+#endif // WEAPON_MORTAR_H
diff --git a/game/shared/tf2/weapon_obj_empgenerator.cpp b/game/shared/tf2/weapon_obj_empgenerator.cpp
new file mode 100644
index 0000000..e1004da
--- /dev/null
+++ b/game/shared/tf2/weapon_obj_empgenerator.cpp
@@ -0,0 +1,37 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "tf_basecombatweapon.h"
+#include "tf_obj.h"
+#include "tf_obj_empgenerator.h"
+#include "weapon_basecombatobject.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: Combat object weapon for the EMP Generator
+//-----------------------------------------------------------------------------
+class CWeaponObjEMPGenerator : public CWeaponBaseCombatObject
+{
+ DECLARE_CLASS( CWeaponObjEMPGenerator, CWeaponBaseCombatObject );
+public:
+ CWeaponObjEMPGenerator( void );
+
+ DECLARE_SERVERCLASS();
+};
+
+IMPLEMENT_SERVERCLASS_ST( CWeaponObjEMPGenerator, DT_WeaponObjEMPGenerator )
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_obj_empgenerator, CWeaponObjEMPGenerator );
+PRECACHE_WEAPON_REGISTER(weapon_obj_empgenerator);
+
+CWeaponObjEMPGenerator::CWeaponObjEMPGenerator( void )
+{
+ m_szObjectName = "obj_empgenerator";
+ m_vecBuildMins = EMPGENERATOR_MINS - Vector( 4,4,4 );
+ m_vecBuildMaxs = EMPGENERATOR_MAXS + Vector( 4,4,4 );
+} \ No newline at end of file
diff --git a/game/shared/tf2/weapon_obj_rallyflag.cpp b/game/shared/tf2/weapon_obj_rallyflag.cpp
new file mode 100644
index 0000000..6a041ef
--- /dev/null
+++ b/game/shared/tf2/weapon_obj_rallyflag.cpp
@@ -0,0 +1,74 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_basecombatobject.h"
+
+#if !defined( CLIENT_DLL )
+// #include "grenade_antipersonnel.h"
+#else
+
+#define CWeaponObjRallyFlag C_WeaponObjRallyFlag
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Combat object weapon for the Rally Flag
+//-----------------------------------------------------------------------------
+class CWeaponObjRallyFlag : public CWeaponBaseCombatObject
+{
+ DECLARE_CLASS( CWeaponObjRallyFlag, CWeaponBaseCombatObject );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponObjRallyFlag( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+private:
+ CWeaponObjRallyFlag( const CWeaponObjRallyFlag & );
+
+};
+
+CWeaponObjRallyFlag::CWeaponObjRallyFlag( void )
+{
+ m_szObjectName = "obj_rallyflag";
+ m_vecBuildMins = RALLYFLAG_MINS - Vector( 4,4,4 );
+ m_vecBuildMaxs = RALLYFLAG_MAXS + Vector( 4,4,4 );
+ SetPredictionEligible( true );
+}
+
+LINK_ENTITY_TO_CLASS( weapon_obj_rallyflag, CWeaponObjRallyFlag );
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponObjRallyFlag, DT_WeaponObjRallyFlag )
+
+BEGIN_NETWORK_TABLE( CWeaponObjRallyFlag, DT_WeaponObjRallyFlag )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponObjRallyFlag )
+END_PREDICTION_DATA()
+
+PRECACHE_WEAPON_REGISTER(weapon_obj_rallyflag);
diff --git a/game/shared/tf2/weapon_objectselection.cpp b/game/shared/tf2/weapon_objectselection.cpp
new file mode 100644
index 0000000..e04e8cb
--- /dev/null
+++ b/game/shared/tf2/weapon_objectselection.cpp
@@ -0,0 +1,216 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "basetfcombatweapon_shared.h"
+
+#if defined( CLIENT_DLL )
+#include "hud.h"
+#include <vgui_controls/Controls.h>
+#include <vgui/ISurface.h>
+#endif
+
+#include "weapon_objectselection.h"
+
+#if !defined( CLIENT_DLL )
+#include "weapon_builder.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+LINK_ENTITY_TO_CLASS( weapon_objectselection, CWeaponObjectSelection );
+
+PRECACHE_WEAPON_REGISTER( weapon_objectselection );
+
+BEGIN_PREDICTION_DATA( CWeaponObjectSelection )
+END_PREDICTION_DATA()
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponObjectSelection, DT_WeaponObjectSelection )
+
+BEGIN_NETWORK_TABLE( CWeaponObjectSelection, DT_WeaponObjectSelection )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO( m_iObjectType ), 8, SPROP_UNSIGNED ),
+#else
+ RecvPropInt( RECVINFO( m_iObjectType ) ),
+#endif
+END_NETWORK_TABLE()
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponObjectSelection::CWeaponObjectSelection()
+{
+#if defined( CLIENT_DLL )
+ m_bNeedSpriteSetup = true;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deploy the builder weapon instead of this weapon, and tell it to switch to this object.
+//-----------------------------------------------------------------------------
+bool CWeaponObjectSelection::CanDeploy( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if (!pPlayer)
+ return false;
+
+#if !defined(CLIENT_DLL)
+ // Select a state for the builder weapon
+ CWeaponBuilder *pBuilder = pPlayer->GetWeaponBuilder();
+ if ( pBuilder )
+ {
+ pBuilder->SetCurrentObject( m_iObjectType );
+ pPlayer->Weapon_Switch( pBuilder );
+ pPlayer->SetNextAttack( gpGlobals->curtime );
+ }
+#endif
+
+ // Never deploy this weapon
+ return false;
+}
+
+
+void CWeaponObjectSelection::SetType( int iType )
+{
+ m_iObjectType = iType;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Setup the weapon selection sprite we should use for this weapon
+//-----------------------------------------------------------------------------
+void CWeaponObjectSelection::SetupObjectSelectionSprite( void )
+{
+#ifdef CLIENT_DLL
+ // Use the sprite details from the text file, with a custom sprite
+ char *iconTexture = GetObjectInfo( m_iObjectType )->m_pIconActive;
+ if ( iconTexture && iconTexture[ 0 ] )
+ {
+ m_pSelectionTexture = gHUD.GetIcon( iconTexture );
+ }
+ else
+ {
+ m_pSelectionTexture = NULL;
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this weapon has some ammo
+//-----------------------------------------------------------------------------
+bool CWeaponObjectSelection::HasAmmo( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return false;
+
+ int iCost = CalculateObjectCost( m_iObjectType, pOwner->GetNumObjects(m_iObjectType), pOwner->GetTeamNumber() );
+ return ( pOwner->GetBankResources() >= iCost );
+}
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponObjectSelection::PreDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PreDataUpdate(updateType);
+
+ m_iOldObjectType = m_iObjectType;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void CWeaponObjectSelection::PostDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PostDataUpdate( updateType );
+
+ // Note, can't do this in OnDataChanged since you can get multiple
+ // predataupdates/postdataupdates in one frame but only one OnDataChanged just before
+ // rendering
+ if ( m_iOldObjectType != m_iObjectType )
+ {
+ m_bNeedSpriteSetup = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponObjectSelection::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged(updateType);
+
+ // If our type has changed or we've never been set up, then
+ // setup our object selection sprite
+ if ( m_bNeedSpriteSetup)
+ {
+ m_bNeedSpriteSetup = false;
+ SetupObjectSelectionSprite();
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CWeaponObjectSelection::GetSubType( void )
+{
+#if !defined( CLIENT_DLL )
+ return BaseClass::GetSubType();
+#else
+ // We don't network down the subtype, so use out object type
+ return m_iObjectType;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CWeaponObjectSelection::GetSlot( void ) const
+{
+ return GetObjectInfo( m_iObjectType )->m_SelectionSlot;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CWeaponObjectSelection::GetPosition( void ) const
+{
+ return GetObjectInfo( m_iObjectType )->m_SelectionPosition;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : char const
+//-----------------------------------------------------------------------------
+const char *CWeaponObjectSelection::GetPrintName( void ) const
+{
+ return GetObjectInfo( m_iObjectType )->m_pStatusName;
+}
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHudTexture const * CWeaponObjectSelection::GetSpriteActive( void ) const
+{
+ return m_pSelectionTexture;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHudTexture const * CWeaponObjectSelection::GetSpriteInactive( void ) const
+{
+ return m_pSelectionTexture;
+}
+
+#endif \ No newline at end of file
diff --git a/game/shared/tf2/weapon_objectselection.h b/game/shared/tf2/weapon_objectselection.h
new file mode 100644
index 0000000..90f86bb
--- /dev/null
+++ b/game/shared/tf2/weapon_objectselection.h
@@ -0,0 +1,69 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_OBJECTSELECTION_H
+#define WEAPON_OBJECTSELECTION_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#if defined( CLIENT_DLL )
+#define CWeaponObjectSelection C_WeaponObjectSelection
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose: This is a 'weapon' that's used to put objects into the client's weapon selection
+//-----------------------------------------------------------------------------
+class CWeaponObjectSelection : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponObjectSelection, CBaseTFCombatWeapon );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponObjectSelection();
+
+#if defined( CLIENT_DLL )
+ virtual void PreDataUpdate( DataUpdateType_t updateType );
+ virtual void PostDataUpdate( DataUpdateType_t updateType );
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+#endif
+
+ // Overridden to handle objects
+ virtual bool CanDeploy( void );
+ virtual bool HasAmmo( void );
+ virtual int GetSubType( void );
+ virtual int GetSlot( void ) const;
+ virtual int GetPosition( void ) const;
+ virtual const char *GetPrintName( void ) const;
+
+#ifdef CLIENT_DLL
+ virtual CHudTexture const *GetSpriteActive( void ) const;
+ virtual CHudTexture const *GetSpriteInactive( void ) const;
+#endif
+
+ // Set this selection's object type
+ void SetType( int iType );
+ void SetupObjectSelectionSprite( void );
+
+ virtual bool SupportsTwoHanded( void ) { return true; };
+
+protected:
+ CNetworkVar( int, m_iObjectType );
+ int m_iOldObjectType;
+
+#ifdef CLIENT_DLL
+ CHudTexture *m_pSelectionTexture;
+ bool m_bNeedSpriteSetup;
+ vgui::HFont m_hNumberFont;
+#endif
+
+private:
+ CWeaponObjectSelection( const CWeaponObjectSelection & );
+};
+
+#endif // WEAPON_OBJECTSELECTION_H
diff --git a/game/shared/tf2/weapon_pistols.cpp b/game/shared/tf2/weapon_pistols.cpp
new file mode 100644
index 0000000..47c51a9
--- /dev/null
+++ b/game/shared/tf2/weapon_pistols.cpp
@@ -0,0 +1,134 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Recon's dual semi-auto pistols
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "tf_basecombatweapon.h"
+#include "in_buttons.h"
+#include "gamerules.h"
+#include "ammodef.h"
+#include "basegrenade_shared.h"
+
+ConVar weapon_pistols_damage( "weapon_pistols_damage","0", FCVAR_NONE, "Recon pistols maximum damage" );
+ConVar weapon_pistols_range( "weapon_pistols_range","0", FCVAR_NONE, "Recon pistols maximum range" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponPistols : public CTFMachineGun
+{
+ DECLARE_CLASS( CWeaponPistols, CTFMachineGun );
+public:
+ virtual float GetFireRate( void );
+ virtual const Vector& GetBulletSpread( void );
+ virtual bool Deploy( void );
+ virtual void PrimaryAttack( void );
+ virtual void SecondaryAttack( void );
+ virtual void ItemPostFrame( void );
+
+private:
+ float m_flSoonestPrimaryAttack;
+ float m_flLastPrimaryAttack;
+};
+
+LINK_ENTITY_TO_CLASS( weapon_pistols, CWeaponPistols );
+PRECACHE_WEAPON_REGISTER(weapon_pistols);
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the accuracy derived from weapon and player, and return it
+//-----------------------------------------------------------------------------
+const Vector& CWeaponPistols::GetBulletSpread( void )
+{
+ static Vector cone;
+ cone = VECTOR_CONE_10DEGREES;
+
+ // Modify accuracy based upon firing rate
+ // Maximum accuracy's used if you're firing at the standard rate of the gun
+ float flModifier = MIN( GetFireRate(), gpGlobals->curtime - m_flLastPrimaryAttack );
+ flModifier = 1.0 - RemapVal( flModifier, 0, GetFireRate(), 0, 1.0 );
+ cone *= flModifier;
+
+ return cone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponPistols::GetFireRate( void )
+{
+ return 0.5;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+bool CWeaponPistols::Deploy( void )
+{
+ m_flSoonestPrimaryAttack = gpGlobals->curtime;
+ m_flLastPrimaryAttack = gpGlobals->curtime;
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistols::PrimaryAttack( void )
+{
+ m_flSoonestPrimaryAttack = gpGlobals->curtime + 0.1;
+ BaseClass::PrimaryAttack();
+ m_flLastPrimaryAttack = gpGlobals->curtime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Throw out a sticky bomb
+//-----------------------------------------------------------------------------
+void CWeaponPistols::SecondaryAttack( void )
+{
+ /* FIXME: Temporarily disabled
+ CBasePlayer *pPlayer = GetOwner();
+ if ( pPlayer == NULL )
+ return;
+
+ // Calculate position & velocity
+ Vector vecForward;
+ pPlayer->EyeVectors( &vecForward );
+ Vector vecOrigin = pPlayer->EyePosition();
+ vecOrigin += (vecForward * 16);
+
+ // Create the stickybomb
+ CBaseGrenade *pGrenade = (CBaseGrenade*)CreateEntityByName("grenade_stickybomb");
+ UTIL_SetOrigin( pGrenade->pev, vecOrigin );
+ pGrenade->Spawn();
+ pGrenade->SetOwnerEntity( pPlayer );
+ pGrenade->m_hOwner = pPlayer;
+ pGrenade->m_vecVelocity = vecForward * 1200;
+ VectorAngles( vecForward, pGrenade->GetAngles() );
+
+ // Reduce ammo, setup for refire
+ pPlayer->m_iAmmo[m_iSecondaryAmmoType]--;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPistols::ItemPostFrame( void )
+{
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( pPlayer == NULL )
+ return;
+
+ // Allow a refire as fast as the player can click
+ if ( ( ( pPlayer->m_nButtons & IN_ATTACK ) == false ) && ( m_flSoonestPrimaryAttack < gpGlobals->curtime )
+ /* && ( m_flNextSecondaryAttack < gpGlobals->curtime )*/ )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime - 0.1f;
+ }
+
+ BaseClass::ItemPostFrame();
+}
+
diff --git a/game/shared/tf2/weapon_plasmarifle.cpp b/game/shared/tf2/weapon_plasmarifle.cpp
new file mode 100644
index 0000000..d89f7f5
--- /dev/null
+++ b/game/shared/tf2/weapon_plasmarifle.cpp
@@ -0,0 +1,74 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Medic's plasma rifle
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "tf_player.h"
+#include "in_buttons.h"
+#include "gamerules.h"
+#include "ammodef.h"
+#include "entitylist.h"
+#include "plasmaprojectile.h"
+#include "tf_shareddefs.h"
+#include "basegrenade_shared.h"
+#include "tf_basecombatweapon.h"
+
+// Damage CVars
+ConVar weapon_plasmarifle_damage( "weapon_plasmarifle_damage","0", FCVAR_NONE, "Plasma Rifle maximum damage" );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponPlasmaRifle : public CTFMachineGun
+{
+ DECLARE_CLASS( CWeaponPlasmaRifle, CTFMachineGun );
+public:
+ virtual void FireBullets( CBaseTFCombatWeapon *pWeapon, int cShots, const Vector &vecSrc, const Vector &vecDirShooting, const Vector &vecSpread, float flDistance, int iBulletType, int iTracerFreq);
+ virtual const Vector& GetBulletSpread( void );
+ virtual float GetFireRate( void );
+};
+
+LINK_ENTITY_TO_CLASS( weapon_plasmarifle, CWeaponPlasmaRifle );
+PRECACHE_WEAPON_REGISTER(weapon_plasmarifle);
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponPlasmaRifle::FireBullets( CBaseTFCombatWeapon *pWeapon, int cShots, const Vector &vecSrc, const Vector &vecDirShooting, const Vector &vecSpread, float flDistance, int iBulletType, int iTracerFreq )
+{
+ CBaseTFPlayer *pPlayer = static_cast< CBaseTFPlayer * >( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ Vector vecForward;
+ pPlayer->EyeVectors( &vecForward );
+ Vector vecOrigin = pPlayer->EyePosition();
+ trace_t tr;
+
+ Vector vecTracerSrc = pWeapon->GetTracerSrc( (Vector&)vecSrc, (Vector&)vecDirShooting );
+
+ // Fire the emp projectile
+ CBasePlasmaProjectile *pPlasma = CBasePlasmaProjectile::Create( vecTracerSrc, vecDirShooting, DMG_ENERGYBEAM, pPlayer );
+ pPlasma->SetDamage( weapon_plasmarifle_damage.GetFloat() );
+ pPlasma->m_hOwner = pPlayer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the accuracy derived from weapon and player, and return it
+//-----------------------------------------------------------------------------
+const Vector& CWeaponPlasmaRifle::GetBulletSpread( void )
+{
+ static Vector cone = VECTOR_CONE_4DEGREES;
+ return cone;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponPlasmaRifle::GetFireRate( void )
+{
+ return 0.2;
+}
+
diff --git a/game/shared/tf2/weapon_repairgun.cpp b/game/shared/tf2/weapon_repairgun.cpp
new file mode 100644
index 0000000..b2e626d
--- /dev/null
+++ b/game/shared/tf2/weapon_repairgun.cpp
@@ -0,0 +1,740 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The Medic's Medikit weapon
+//
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "in_buttons.h"
+#include "weapon_combatshield.h"
+#include "engine/IEngineSound.h"
+#include "grenade_base_empable.h"
+#include "basetfvehicle.h"
+#include "tf_gamerules.h"
+
+//#define REPAIR_GUN_DISABLES_GRENADES // Uncomment if you want the repair gun to disable grenades.
+
+#if defined( CLIENT_DLL )
+#include "particles_simple.h"
+#else
+#include "ndebugoverlay.h"
+#endif
+
+#include "weapon_repairgun.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Buff ranges
+ConVar weapon_repairgun_target_range( "weapon_repairgun_target_range", "450", FCVAR_REPLICATED, "The farthest away you can be for the repair gun to initially lock onto a player." );
+ConVar weapon_repairgun_stick_range( "weapon_repairgun_stick_range", "512", FCVAR_REPLICATED, "How far away the repair gun can stay locked onto someone." );
+ConVar weapon_repairgun_rate( "weapon_repairgun_rate", "12", FCVAR_REPLICATED, "Health healed per second by the repair gun." );
+ConVar weapon_repairgun_damage_modifier( "weapon_repairgun_damage_modifier", "1.5", FCVAR_REPLICATED, "Scales the damage a player does while being healed with the repair gun." );
+ConVar weapon_repairgun_debug( "weapon_repairgun_debug", "0", FCVAR_REPLICATED, "Show debugging info for the repair gun." );
+ConVar weapon_repairgun_construction_rate( "weapon_repairgun_construction_rate", "10", FCVAR_REPLICATED, "Constructing object health healed per second by the repair gun." );
+
+LINK_ENTITY_TO_CLASS( weapon_repairgun, CWeaponRepairGun );
+
+PRECACHE_WEAPON_REGISTER( weapon_repairgun );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRepairGun, DT_WeaponRepairGun )
+
+BEGIN_NETWORK_TABLE( CWeaponRepairGun, DT_WeaponRepairGun )
+#if !defined( CLIENT_DLL )
+ SendPropInt( SENDINFO( m_bHealing ), 1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_bAttacking ), 1, SPROP_UNSIGNED ),
+ SendPropEHandle( SENDINFO( m_hHealingTarget ) ),
+#else
+ RecvPropInt( RECVINFO( m_bAttacking ) ),
+ RecvPropInt( RECVINFO( m_bHealing ) ),
+ RecvPropEHandle( RECVINFO(m_hHealingTarget) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponRepairGun )
+
+ DEFINE_PRED_FIELD( m_bHealing, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bAttacking, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_hHealingTarget, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_FIELD( m_flHealEffectLifetime, FIELD_FLOAT ),
+
+// DEFINE_PRED_FIELD( m_pEmitter, FIELD_POINTER ),
+// DEFINE_PRED_FIELD( m_hParticleMaterial, FIELD_???, ),
+// DEFINE_PRED_FIELD( m_PathParticleEvent, FIELD_???, ),
+// DEFINE_PRED_FIELD( m_bPlayingSound, FIELD_BOOLEAN ),
+
+END_PREDICTION_DATA()
+
+#define PARTICLE_PATH_VEL 140.0
+#define NUM_PATH_PARTICLES_PER_SEC 600.0f
+#define NUM_TARGET_PARTICLES_PER_SEC 720.0f
+
+#define NUM_REPAIRGUN_PATH_POINTS 8
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponRepairGun::CWeaponRepairGun( void )
+{
+ m_flHealEffectLifetime = 0;
+
+ m_bHealing = false;
+ m_bAttacking = false;
+
+ m_flNextBuzzTime = 0;
+
+#if defined( CLIENT_DLL )
+ m_pEmitter = NULL;
+ m_hParticleMaterial = INVALID_MATERIAL_HANDLE;
+ m_PathParticleEvent.Init( NUM_PATH_PARTICLES_PER_SEC );
+ m_bPlayingSound = false;
+#endif
+
+ SetPredictionEligible( true );
+}
+
+
+void CWeaponRepairGun::Precache()
+{
+ BaseClass::Precache();
+ PrecacheScriptSound( "WeaponRepairGun.NoTarget" );
+ PrecacheScriptSound( "WeaponRepairGun.Healing" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRepairGun::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ RemoveHealingTarget();
+ m_bAttacking = false;
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponRepairGun::GetTargetRange( void )
+{
+ return weapon_repairgun_target_range.GetFloat();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponRepairGun::GetStickRange( void )
+{
+ return weapon_repairgun_stick_range.GetFloat();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponRepairGun::GetHealRate( void )
+{
+ return weapon_repairgun_rate.GetFloat();
+}
+
+// Now make sure there isn't something other than team players in the way.
+class CRepairFilter : public CTraceFilterSimple
+{
+public:
+ CRepairFilter( CBaseEntity *pShooter ) : CTraceFilterSimple( pShooter, TFCOLLISION_GROUP_WEAPON )
+ {
+ m_pShooter = pShooter;
+ }
+
+ virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
+ {
+ // If it hit an edict the isn't the target and is on our team, then the ray is blocked.
+ CBaseEntity *pEnt = static_cast<CBaseEntity*>(pHandleEntity);
+
+ // Ignore collisions with the shooter
+ if ( pEnt == m_pShooter )
+ return false;
+
+ // You can't heal a vehicle you are sitting in.
+
+ if( ((CBaseTFPlayer*)m_pShooter)->IsInAVehicle() )
+ {
+
+ CBaseEntity* pVehicle = ((CBaseTFPlayer*)m_pShooter)->GetVehicle()->GetVehicleEnt();
+ if( pVehicle == pEnt )
+ {
+ return false;
+ }
+ }
+
+#ifdef REPAIR_GUN_DISABLES_GRENADES
+
+ // Repairgun can also disable enemy grenades
+ CBaseEMPableGrenade *pGrenade = dynamic_cast<CBaseEMPableGrenade*>(pEnt);
+
+ // Ignore collisions with teammates, or friendly grenades
+ if ( !pGrenade )
+ {
+ if ( pEnt->GetTeam() == m_pShooter->GetTeam() )
+ return false;
+ }
+#else
+ if ( pEnt->GetTeam() == m_pShooter->GetTeam() )
+ return false;
+#endif
+
+ return CTraceFilterSimple::ShouldHitEntity( pHandleEntity, contentsMask );
+ }
+
+ CBaseEntity *m_pShooter;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Vehicle checking to see if we should switch targets from a player
+// to a vehicle, or vice versa.
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponRepairGun::CheckVehicleTargets( CBaseEntity *pCurHealing )
+{
+ // Unable to switch to/from players?
+ if ( !TargetsPlayers() )
+ return pCurHealing;
+
+ CBaseTFVehicle *pTargetVehicle = NULL;
+
+ // If we're a fully healed player sitting in a vehicle, see if the vehicle needs healing instead
+ if ( pCurHealing->IsPlayer() && pCurHealing->GetHealth() >= pCurHealing->GetMaxHealth() )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)pCurHealing;
+ if ( !pPlayer->IsInAVehicle() )
+ return pCurHealing;
+
+ pTargetVehicle = (CBaseTFVehicle*)(pPlayer->GetVehicle()->GetVehicleEnt());
+ if ( pTargetVehicle->GetHealth() < pTargetVehicle->GetMaxHealth() )
+ return pTargetVehicle;
+ }
+ else
+ {
+ // If the entity is a vehicle, and it's fully healed, heal any players in it instead
+ pTargetVehicle = dynamic_cast<CBaseTFVehicle *>(pCurHealing);
+ }
+
+ // Is the vehicle fully healed?
+ if ( pTargetVehicle && pTargetVehicle->GetHealth() >= pTargetVehicle->GetMaxHealth() )
+ {
+ CBaseTFPlayer *pUnhurtPlayer = NULL;
+ CBaseTFPlayer *pHurtPlayer = NULL;
+
+ // Go through all players in the vehicle
+ int iMax = pTargetVehicle->GetMaxPassengerCount();
+ for ( int i = 0; i < iMax; i++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)pTargetVehicle->GetPassenger(i);
+ if ( pPlayer )
+ {
+ if ( pPlayer->GetHealth() < pPlayer->GetMaxHealth() )
+ {
+ pUnhurtPlayer = pPlayer;
+ }
+ else
+ {
+ pHurtPlayer = pPlayer;
+ }
+ }
+ }
+
+ // Heal hurt players first
+ if ( pHurtPlayer )
+ return pHurtPlayer;
+
+ if ( pUnhurtPlayer )
+ return pUnhurtPlayer;
+ }
+
+ return pCurHealing;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns a pointer to a healable target
+//-----------------------------------------------------------------------------
+CBaseEntity *CWeaponRepairGun::GetTargetToHeal( CBaseEntity *pCurHealing )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return NULL;
+
+ Vector vecSrc = pOwner->Weapon_ShootPosition( );
+
+
+ // If we're already healing someone, stick onto them as long as possible.
+ // Even if we can't heal them at the moment, lock onto them until they release the buttom.
+ CBaseEntity* pTarget = pCurHealing;
+ CBaseEntity* pVehicle = NULL; // Vehicle the owner is in, or NULL.
+
+ // You can't heal a vehicle you are sitting in, make sure we aren't healing a vehicle right after we've gotten in.
+ if( ((CBaseTFPlayer*)pOwner)->IsInAVehicle() )
+ {
+
+ pVehicle = ((CBaseTFPlayer*)pOwner)->GetVehicle()->GetVehicleEnt();
+ if( pVehicle == pTarget )
+ {
+ pTarget = NULL;
+ }
+ }
+
+ if ( pTarget && pTarget->IsAlive() && (pTarget->GetTeam() == pOwner->GetTeam()) )
+ {
+ // Make sure the guy didn't go out of range.
+ Vector vecTargetPoint = pTarget->WorldSpaceCenter();
+ Vector vecPoint;
+
+ // If it's brush built, use absmins/absmaxs
+ pTarget->CollisionProp()->CalcNearestPoint( vecSrc, &vecPoint );
+
+#ifndef CLIENT_DLL
+ //NDebugOverlay::Box( vecPoint, Vector(-2,-2,-2), Vector(2,2,2), 255,0,0, 8, 0.1 );
+#endif
+
+ float flDistance = (vecPoint - vecSrc).Length();
+ if ( flDistance < GetStickRange() )
+ {
+ trace_t tr;
+ CRepairFilter drainFilter( pOwner );
+ UTIL_TraceLine( vecSrc, vecTargetPoint, MASK_SHOT, &drainFilter, &tr );
+
+ if (( tr.fraction == 1.0f) || (tr.m_pEnt == pTarget))
+ return CheckVehicleTargets( pTarget );
+ }
+
+ // Return null so we can't heal this player but m_hHealingPlayer stays set to them.
+ return NULL;
+ }
+ else
+ {
+ // Ok, try to find a new player to heal.
+ // Get the target point and location
+ Vector vecAiming;
+ pOwner->EyeVectors( &vecAiming );
+
+ // Find a player in range of this player, and make sure they're healable.
+ Vector vecEnd = vecSrc + vecAiming * GetTargetRange();
+ trace_t tr;
+
+ // Use WeaponTraceLine so shields are tested...
+ TFGameRules()->WeaponTraceLine( vecSrc, vecEnd, (MASK_SHOT & ~CONTENTS_HITBOX), pOwner, DMG_PROBE, &tr );
+
+#ifndef CLIENT_DLL
+ //NDebugOverlay::Box( vecSrc, Vector(-2,-2,-2), Vector(2,2,2), 192,192,0, 8, 10 );
+ //NDebugOverlay::Box( vecEnd, Vector(-2,-2,-2), Vector(2,2,2), 0,255,0, 8, 10 );
+ //NDebugOverlay::Box( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,0,255, 8, 10 );
+#endif
+
+ if ( tr.fraction != 1.0 )
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if ( pEntity )
+ {
+ // Repairgun can also disable enemy grenades
+#ifdef REPAIR_GUN_DISABLES_GRENADES
+
+ CBaseEMPableGrenade *pGrenade = dynamic_cast<CBaseEMPableGrenade*>(pEntity);
+ if ( pGrenade && !pGrenade->InSameTeam( pOwner ) )
+ return pGrenade;
+#endif
+
+ // Only target players if I'm allowed to
+ if ( !TargetsPlayers() && pEntity->IsPlayer() )
+ return NULL;
+
+ // You can't heal a vehicle you are sitting in.
+ if ( pVehicle && ( pVehicle == pEntity ) )
+ return NULL;
+
+ if ( (pEntity != pOwner) && pEntity->IsAlive() && pEntity->CanBePoweredUp() )
+ {
+ // Target needs to be on the same team
+ if ( pEntity->InSameTeam( pOwner ) )
+ return CheckVehicleTargets( pEntity );
+ }
+ }
+ }
+
+ if ( weapon_repairgun_debug.GetBool() )
+ {
+ ClientPrint( pOwner, HUD_PRINTCENTER, "REPAIRGUN: no target found\n" );
+ }
+
+ return NULL;
+ }
+
+ return NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Overloaded to handle the hold-down healing
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+#if !defined( CLIENT_DLL )
+ if ( AppliesModifier() )
+ {
+ m_DamageModifier.SetModifier( weapon_repairgun_damage_modifier.GetFloat() );
+ }
+#endif
+
+ // Try to start healing
+ m_bAttacking = false;
+ if ( (pOwner->m_nButtons & IN_ATTACK) && GetShieldState() == SS_DOWN )
+ {
+ PrimaryAttack();
+ m_bAttacking = true;
+ }
+ else if ( GetCurHealingTarget() )
+ {
+ // Detach from the player if they release the attack button.
+ RemoveHealingTarget();
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're healing
+ AllowShieldPostFrame( !m_bAttacking );
+
+ WeaponIdle();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::RemoveHealingTarget()
+{
+ // Stop the welding animation
+ if ( m_bHealing )
+ {
+ SendWeaponAnim( ACT_FIRE_END );
+ }
+
+ m_hHealingTarget = NULL;
+#if !defined( CLIENT_DLL )
+ m_DamageModifier.RemoveModifier();
+#endif
+ m_bHealing = false;
+
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( pOwner )
+ {
+ pOwner->SetIDEnt( NULL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CWeaponRepairGun::ComputeEMPFireState( void )
+{
+ if ( IsOwnerEMPed() )
+ {
+ // If we've just been EMPed, remove the heal target
+ if ( GetCurHealingTarget() )
+ {
+ RemoveHealingTarget();
+ }
+ return false;
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Attempt to heal any player within range of the medikit
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Can't fire if we've been EMPed.
+ if ( !ComputeEMPFireState() )
+ return;
+
+#if !defined( CLIENT_DLL )
+ // Find a Player to buff with hitscan
+ CBaseEntity *pCurHealing = GetCurHealingTarget();
+ CBaseEntity *pTarget = GetTargetToHeal( pCurHealing );
+ if ( pTarget )
+ {
+ // Start the welding animation
+ if ( !m_bHealing )
+ {
+ SendWeaponAnim( ACT_FIRE_START );
+ }
+
+ // Tell the client who we're trying to heal.
+ m_bHealing = true;
+ m_hHealingTarget = pTarget;
+
+ // Repairgun needs to EMP grenades, heal everything else
+#ifdef REPAIR_GUN_DISABLES_GRENADES
+
+ CBaseEMPableGrenade *pGrenade = dynamic_cast<CBaseEMPableGrenade*>(pTarget);
+ if ( pGrenade )
+ {
+ pTarget->TakeEMPDamage( 1.0 );
+ }
+ else
+#endif
+ {
+ // Can't bring things back from the dead
+ if ( pTarget->IsAlive() )
+ {
+ float flBoostAmount = GetHealRate() * gpGlobals->frametime;
+ // If it's an object, and it's constructing, use the construction heal rate
+ if ( pTarget->Classify() == CLASS_MILITARY )
+ {
+ CBaseObject *pObject = dynamic_cast<CBaseObject*>(pTarget);
+ if ( pObject && pObject->IsBuilding() )
+ {
+ flBoostAmount = weapon_repairgun_construction_rate.GetFloat() * gpGlobals->frametime;
+ }
+ if ( pObject && pObject->IsDying() )
+ {
+ flBoostAmount = 0;
+ }
+ }
+
+ // If we're not succeeding, remove the damage modifier
+ if ( !pTarget->AttemptToPowerup( POWERUP_BOOST, 1.0, flBoostAmount, pOwner, AppliesModifier() ? &m_DamageModifier : NULL ) )
+ {
+ m_DamageModifier.RemoveModifier();
+ }
+
+ // Force the player's ID target to the heal target
+ pOwner->SetIDEnt( pTarget );
+ }
+ }
+ }
+ else
+ {
+ RemoveHealingTarget();
+ }
+#endif
+
+ CheckRemoveDisguise();
+}
+
+void CWeaponRepairGun::PlayAttackAnimation( int activity )
+{
+ SendWeaponAnim( activity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Idle tests to see if we're facing a valid target for the medikit
+// If so, move into the "heal-able" animation.
+// Otherwise, move into the "not-heal-able" animation.
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::WeaponIdle( void )
+{
+ if ( HasWeaponIdleTimeElapsed() )
+ {
+ // Loop the welding animation
+ if ( m_bHealing )
+ {
+ SendWeaponAnim( ACT_FIRE_LOOP );
+ }
+ else
+ {
+ // select an idle animation
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The player holding this weapon has just gained new technology.
+// Check to see if it affects the medikit
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+}
+
+
+#if defined( CLIENT_DLL )
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRepairGun::StopRepairSound( bool bStopHealingSound, bool bStopNoTargetSound )
+{
+ if ( bStopHealingSound )
+ {
+ StopSound( "WeaponRepairGun.Healing" );
+ }
+
+ if ( bStopNoTargetSound )
+ {
+ StopSound( "WeaponRepairGun.NoTarget" );
+ }
+}
+
+
+void C_WeaponRepairGun::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ // Stop emitting particles if we're going dormant.
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_NEVER );
+ }
+
+ BaseClass::NotifyShouldTransmit( state );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void C_WeaponRepairGun::OnDataChanged( DataUpdateType_t updateType )
+{
+ BaseClass::OnDataChanged( updateType );
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ m_pEmitter = CSimpleEmitter::Create( "C_WeaponRepairGun" );
+
+ m_hParticleMaterial = m_pEmitter->GetPMaterial( "sprites/chargeball" );
+ }
+
+ // Think?
+ if ( m_bHealing && m_hHealingTarget.Get())
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_ALWAYS );
+ }
+ else
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_NEVER );
+ m_bPlayingSound = false;
+ StopRepairSound( true, false );
+
+ // Are they holding the attack button but not healing anyone? Give feedback.
+ if ( IsActiveByLocalPlayer() && GetOwner() && GetOwner()->IsAlive() && m_bAttacking && GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ {
+ if ( gpGlobals->curtime >= m_flNextBuzzTime )
+ {
+ CLocalPlayerFilter filter;
+ EmitSound( filter, entindex(), "WeaponRepairGun.NoTarget" );
+ m_flNextBuzzTime = gpGlobals->curtime + 0.5f; // only buzz every so often.
+ }
+ }
+ else
+ {
+ StopRepairSound( false, true ); // Stop the "no target" sound.
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_WeaponRepairGun::ClientThink()
+{
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( !pPlayer )
+ return;
+
+ if ( m_hHealingTarget == NULL )
+ {
+ return;
+ }
+
+ // Don't show it while the player is dead. Ideally, we'd respond to m_bHealing in OnDataChanged,
+ // but it stops sending the weapon when it's holstered, and it gets holstered when the player dies.
+ C_BasePlayer *pFiringPlayer = dynamic_cast< C_BasePlayer* >( GetOwner() );
+ if ( !pFiringPlayer || pFiringPlayer->IsPlayerDead() )
+ {
+ ClientThinkList()->SetNextClientThink( GetClientHandle(), CLIENT_THINK_NEVER );
+ m_bPlayingSound = false;
+ StopRepairSound();
+ return;
+ }
+
+ if ( !m_bPlayingSound )
+ {
+ m_bPlayingSound = true;
+ CLocalPlayerFilter filter;
+ EmitSound( filter, entindex(), "WeaponRepairGun.Healing" );
+ }
+
+ Vector points[NUM_REPAIRGUN_PATH_POINTS];
+
+ // First generate a sequence of points so we can parameterize the (curvy) path from the
+ // tip of the gun to the target.
+ Vector vForward, vOrigin;
+ QAngle vAngles;
+ GetShootPosition( vOrigin, vAngles );
+
+ AngleVectors( vAngles, &vForward );
+
+ Vector vecTargetOrg = m_hHealingTarget->WorldSpaceCenter();
+
+ Vector vDirTo = vecTargetOrg - vOrigin;
+ float flDistanceTo = vDirTo.Length();
+ vDirTo /= flDistanceTo;
+
+ float flBendDist = flDistanceTo * 3;
+ const Vector A = vOrigin - vForward * flBendDist;
+ const Vector &B = vOrigin;
+ const Vector &C = vecTargetOrg;
+ const Vector D = vecTargetOrg - vForward * flBendDist;
+
+ for ( int i=0; i < NUM_REPAIRGUN_PATH_POINTS; i++ )
+ {
+ Catmull_Rom_Spline( A, B, C, D, (float)i / (NUM_REPAIRGUN_PATH_POINTS-1), points[i] );
+ }
+
+ // Add random short-lived particles from the gun tip to the target.
+ m_pEmitter->SetSortOrigin( (vecTargetOrg + pPlayer->GetAbsOrigin()) * 0.5f );
+
+ float flCur = gpGlobals->frametime;
+ while ( m_PathParticleEvent.NextEvent( flCur ) )
+ {
+ float t = RandomFloat( 0, 1 );
+ int iPrev = (int)( t * (NUM_REPAIRGUN_PATH_POINTS - 1.001) );
+ float tPrev = (float)iPrev / (NUM_REPAIRGUN_PATH_POINTS - 1);
+ float tNext = (float)(iPrev+1) / (NUM_REPAIRGUN_PATH_POINTS - 1);
+ Assert( tNext <= NUM_REPAIRGUN_PATH_POINTS-1 );
+
+ Vector vPos;
+ VectorLerp( points[iPrev], points[iPrev+1], (t-tPrev) / (tNext - tPrev), vPos );
+ vPos += RandomVector( -3, 3 );
+
+ SimpleParticle *pParticle = m_pEmitter->AddSimpleParticle( m_hParticleMaterial, vPos );
+ if ( pParticle )
+ {
+ // Move the points along the path.
+ pParticle->m_vecVelocity = points[iPrev+1] - points[iPrev];
+ VectorNormalize( pParticle->m_vecVelocity );
+ pParticle->m_vecVelocity *= PARTICLE_PATH_VEL;
+
+ pParticle->m_flRoll = 0;
+ pParticle->m_flRollDelta = 0;
+ pParticle->m_flDieTime = 0.2f;
+ pParticle->m_flLifetime = 0;
+ pParticle->m_uchColor[1] = 200;
+ pParticle->m_uchColor[0] = pParticle->m_uchColor[2] = RandomInt( 0, 128 );
+ pParticle->m_uchStartAlpha = 255;
+ pParticle->m_uchEndAlpha = 0;
+ pParticle->m_uchStartSize = 5;
+ pParticle->m_uchEndSize = 3;
+ pParticle->m_iFlags = 0;
+ }
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/game/shared/tf2/weapon_repairgun.h b/game/shared/tf2/weapon_repairgun.h
new file mode 100644
index 0000000..7a954a6
--- /dev/null
+++ b/game/shared/tf2/weapon_repairgun.h
@@ -0,0 +1,112 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_REPAIRGUN_H
+#define WEAPON_REPAIRGUN_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "weapon_combat_usedwithshieldbase.h"
+
+#if defined( CLIENT_DLL )
+#define CWeaponRepairGun C_WeaponRepairGun
+#endif
+
+//=========================================================
+// Medikit Weapon
+//=========================================================
+class CWeaponRepairGun : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponRepairGun, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponRepairGun( void );
+
+ virtual void Precache();
+
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual void ItemPostFrame( void );
+ virtual void PrimaryAttack( void );
+ virtual void WeaponIdle( void );
+ virtual void PlayAttackAnimation( int activity );
+ virtual void GainedNewTechnology( CBaseTechnology *pTechnology );
+ virtual bool ComputeEMPFireState( void );
+
+ virtual float GetTargetRange( void );
+ virtual float GetStickRange( void );
+ virtual float GetHealRate( void );
+ virtual bool AppliesModifier( void ) { return true; }
+ virtual bool TargetsPlayers( void ) { return true; }
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ // Stop all sounds being output.
+ void StopRepairSound( bool bStopHealingSound = true, bool bStopNoTargetSound = true );
+
+ // This is used to stop making particles when the entity goes dormant.
+ virtual void NotifyShouldTransmit( ShouldTransmitState_t state );
+// IClientNetworkable.
+public:
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+// IClientThinkable.
+public:
+
+ virtual void ClientThink();
+
+#endif
+
+protected:
+ virtual CBaseEntity *GetTargetToHeal( CBaseEntity *pCurHealing );
+ virtual CBaseEntity *CheckVehicleTargets( CBaseEntity *pCurHealing );
+
+private:
+ CBaseEntity* GetCurHealingTarget() { return m_hHealingTarget; }
+ void RemoveHealingTarget();
+
+ double m_flNextBuzzTime;
+
+ float m_flHealEffectLifetime; // Count down until the healing effect goes off.
+#if !defined( CLIENT_DLL )
+ CDamageModifier m_DamageModifier; // This attaches to whoever we're healing.
+#endif
+ CNetworkVar( bool, m_bAttacking );
+
+protected:
+// Networked data.
+ CNetworkVar( bool, m_bHealing );
+ CNetworkHandle( CBaseEntity, m_hHealingTarget );
+
+#if defined( CLIENT_DLL )
+ CSmartPtr<CSimpleEmitter> m_pEmitter;
+ PMaterialHandle m_hParticleMaterial;
+
+ TimedEvent m_PathParticleEvent;
+
+ bool m_bPlayingSound;
+
+#endif
+private:
+ CWeaponRepairGun( const CWeaponRepairGun & );
+};
+
+#endif // WEAPON_REPAIRGUN_H
diff --git a/game/shared/tf2/weapon_rocketlauncher.cpp b/game/shared/tf2/weapon_rocketlauncher.cpp
new file mode 100644
index 0000000..10bc934
--- /dev/null
+++ b/game/shared/tf2/weapon_rocketlauncher.cpp
@@ -0,0 +1,211 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Rocket Launcher (Weapon)
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_combatshield.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "weapon_rocketlauncher.h"
+#include "in_buttons.h"
+
+#include "plasmaprojectile.h"
+#include "weapon_grenade_rocket.h"
+
+#if !defined( CLIENT_DLL )
+// Server Only
+#include "grenade_rocket.h"
+#else
+// Client Only
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+PRECACHE_WEAPON_REGISTER( weapon_rocket_launcher );
+LINK_ENTITY_TO_CLASS( weapon_rocket_launcher, CWeaponRocketLauncher );
+
+BEGIN_PREDICTION_DATA( CWeaponRocketLauncher )
+END_PREDICTION_DATA()
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponRocketLauncher, DT_WeaponRocketLauncher )
+BEGIN_NETWORK_TABLE( CWeaponRocketLauncher, DT_WeaponRocketLauncher )
+//#if !defined( CLIENT_DLL )
+//#else
+//#endif
+END_NETWORK_TABLE()
+
+#if !defined( CLIENT_DLL )
+// Server Only
+ConVar weapon_rocket_launcher_damage( "weapon_rocket_launcher_damage","300", FCVAR_NONE, "Rocker launcher damage" );
+ConVar weapon_rocket_launcher_range( "weapon_rocket_launcher_range", "768", FCVAR_NONE, "Rocket launcher range" );
+#endif
+
+#define WEAPON_ROCKET_LAUNCHER_FIRERATE 1.0f
+#define WEAPON_ROCKET_LAUNCHER_START_AMMO 1
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CWeaponRocketLauncher::CWeaponRocketLauncher()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deconstructor
+//-----------------------------------------------------------------------------
+CWeaponRocketLauncher::~CWeaponRocketLauncher()
+{
+}
+
+//-----------------------------------------------------------------------------/
+// Purpose: Don't fire when EMPed
+//-----------------------------------------------------------------------------
+bool CWeaponRocketLauncher::ComputeEMPFireState( void )
+{
+ if ( IsOwnerEMPed() )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponRocketLauncher::Spawn( void )
+{
+ BaseClass::Spawn();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Handle deploying, undeploying, firing, etc.
+// TODO: Add a deploy to the firing! Currently no reloading!
+//-----------------------------------------------------------------------------
+void CWeaponRocketLauncher::ItemPostFrame( void )
+{
+ // Get the player.
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( UsesClipsForAmmo1() )
+ {
+ CheckReload();
+ }
+
+#if !defined( CLIENT_DLL )
+ if ( !HasPrimaryAmmo() && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
+ {
+ pPlayer->SwitchToNextBestWeapon( NULL );
+ }
+#endif
+
+ // Handle Firing
+ if ( GetShieldState() == SS_DOWN && !m_bInReload )
+ {
+ // Attempting to fire.
+ if ( ( pPlayer->m_nButtons & IN_ATTACK ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) )
+ {
+ if ( m_iClip1 > 0 )
+ {
+ PrimaryAttack();
+ }
+ else
+ {
+ Reload();
+ }
+ }
+
+ // Reload button (or fire button when we're out of ammo)
+ if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
+ {
+ if ( pPlayer->m_nButtons & IN_RELOAD )
+ {
+ Reload();
+ }
+ else if ( !((pPlayer->m_nButtons & IN_ATTACK) || (pPlayer->m_nButtons & IN_ATTACK2) || (pPlayer->m_nButtons & IN_RELOAD)) )
+ {
+ if ( !m_iClip1 && HasPrimaryAmmo() )
+ {
+ Reload();
+ }
+ }
+ }
+ }
+
+ // Prevent shield post frame if we're not ready to attack, or we're charging
+ AllowShieldPostFrame( m_flNextPrimaryAttack <= gpGlobals->curtime || m_bInReload );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Firing
+//-----------------------------------------------------------------------------
+void CWeaponRocketLauncher::PrimaryAttack( void )
+{
+ CBaseTFPlayer *pPlayer = ( CBaseTFPlayer* )GetOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !ComputeEMPFireState() )
+ return;
+
+ // Weapon "Fire" sound.
+ WeaponSound( SINGLE );
+
+ // Play the attack animation (need one for rocket launcher - deploy?)
+ PlayAttackAnimation( GetPrimaryAttackActivity() );
+
+ // Fire the rocket (Get the position and angles).
+ Vector vecFirePos = pPlayer->Weapon_ShootPosition();
+ Vector vecFireAng;
+ pPlayer->EyeVectors( &vecFireAng );
+
+ // Shift it down a bit so the firer can see it
+ Vector vecRight;
+ AngleVectors( pPlayer->EyeAngles() + pPlayer->m_Local.m_vecPunchAngle, NULL, &vecRight, NULL );
+ vecFirePos += Vector( 0, 0, -8 ) + vecRight * 12;
+
+ // Create the rocket.
+#if !defined( CLIENT_DLL )
+ CWeaponGrenadeRocket *pRocket = CWeaponGrenadeRocket::Create( vecFirePos, vecFireAng, weapon_rocket_launcher_range.GetFloat(), pPlayer );
+#else
+ CWeaponGrenadeRocket *pRocket = CWeaponGrenadeRocket::Create( vecFirePos, vecFireAng, 0, pPlayer );
+#endif
+ if ( pRocket )
+ {
+ pRocket->SetRealOwner( pPlayer );
+#if !defined( CLIENT_DLL )
+ pRocket->SetDamage( weapon_rocket_launcher_damage.GetFloat() );
+#endif
+ }
+
+ // Essentially you are done!
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
+ m_iClip1 = m_iClip1 - 1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the rocket launcher firing rate.
+//-----------------------------------------------------------------------------
+float CWeaponRocketLauncher::GetFireRate( void )
+{
+ return WEAPON_ROCKET_LAUNCHER_FIRERATE;
+}
+
+#if defined( CLIENT_DLL )
+// Client Only
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CWeaponRocketLauncher::ShouldPredict( void )
+{
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+}
+
+#endif
diff --git a/game/shared/tf2/weapon_rocketlauncher.h b/game/shared/tf2/weapon_rocketlauncher.h
new file mode 100644
index 0000000..0c42571
--- /dev/null
+++ b/game/shared/tf2/weapon_rocketlauncher.h
@@ -0,0 +1,58 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Rocket Launcher (Weapon)
+//
+//=============================================================================//
+
+#ifndef WEAPON_ROCKETLAUNCHER_H
+#define WEAPON_ROCKETLAUNCHER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetfcombatweapon_shared.h"
+
+#if defined( CLIENT_DLL )
+// Client Only
+ #define CWeaponRocketLauncher C_WeaponRocketLauncher
+#endif
+
+//=============================================================================
+//
+// Rocket Launcher (Weapon)
+//
+class CWeaponRocketLauncher : public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponRocketLauncher, CWeaponCombatUsedWithShieldBase );
+
+public:
+// Client & Server
+
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponRocketLauncher();
+ ~CWeaponRocketLauncher();
+
+ void Spawn( void );
+
+ // All predicted weapons need to implement and return true
+ bool IsPredicted( void ) const { return true; }
+ void PrimaryAttack( void );
+ void ItemPostFrame( void );
+ float GetFireRate( void );
+ bool ComputeEMPFireState( void );
+
+#if defined( CLIENT_DLL )
+// Client Only
+
+ bool ShouldPredict( void );
+#endif
+
+private:
+// Client & Server
+
+ CWeaponRocketLauncher( const CWeaponRocketLauncher& );
+};
+
+#endif // WEAPON_ROCKETLAUNCHER_H \ No newline at end of file
diff --git a/game/shared/tf2/weapon_shield.cpp b/game/shared/tf2/weapon_shield.cpp
new file mode 100644
index 0000000..f711a50
--- /dev/null
+++ b/game/shared/tf2/weapon_shield.cpp
@@ -0,0 +1,218 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The Escort's Shield weapon
+//
+// $Revision: $
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_shield.h"
+#include "in_buttons.h"
+#include <float.h>
+#include "mathlib/mathlib.h"
+#include "engine/IEngineSound.h"
+
+#if defined( CLIENT_DLL )
+#include "c_shield.h"
+#else
+#include "tf_shield.h"
+#endif
+
+//-----------------------------------------------------------------------------
+// Shield WEAPON
+//-----------------------------------------------------------------------------
+LINK_ENTITY_TO_CLASS( weapon_shield, CWeaponShield );
+PRECACHE_WEAPON_REGISTER(weapon_shield);
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponShield, DT_WeaponShield )
+
+BEGIN_NETWORK_TABLE(CWeaponShield, DT_WeaponShield)
+#ifdef CLIENT_DLL
+ RecvPropEHandle (RECVINFO(m_hDeployedShield)),
+#else
+ SendPropEHandle (SENDINFO(m_hDeployedShield)),
+#endif
+END_NETWORK_TABLE()
+
+
+BEGIN_PREDICTION_DATA( CWeaponShield )
+
+ DEFINE_FIELD( m_bIsDeployed, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_bShieldPositionLocked, FIELD_BOOLEAN ),
+
+END_PREDICTION_DATA()
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponShield::CWeaponShield( void )
+{
+ SetPredictionEligible( true );
+ m_bIsDeployed = false;
+}
+
+void CWeaponShield::UpdateOnRemove( void )
+{
+#ifdef GAME_DLL
+ if ( m_hDeployedShield.Get() )
+ {
+ m_hDeployedShield.Get()->Remove( );
+ m_hDeployedShield.Set( NULL );
+ }
+#endif
+
+ // Chain at end to mimic destructor unwind order
+ BaseClass::UpdateOnRemove();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given the distance the hit occurred at, return the damage done
+//-----------------------------------------------------------------------------
+float CWeaponShield::GetDamage( float flDistance, int iLocation )
+{
+ // Can't injure at all over this distance
+ return 0.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponShield::GetFireRate( void )
+{
+ return 1.0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Deploy the shield!
+//-----------------------------------------------------------------------------
+bool CWeaponShield::Deploy( )
+{
+ if ( !BaseClass::Deploy() )
+ return false;
+
+ // NOTE: the underlying system can cause Deploy to be called twice in a row
+ if ( m_bIsDeployed )
+ return true;
+
+ // We gotta make the actual shield effect....
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if ( !pPlayer )
+ return false;
+
+#ifdef GAME_DLL
+ Assert( !m_hDeployedShield.Get().IsValid() );
+ m_hDeployedShield = CreateMobileShield( pPlayer );
+#endif
+
+ SetShieldPositionLocked( false );
+ m_bIsDeployed = true;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove the shield
+//-----------------------------------------------------------------------------
+bool CWeaponShield::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+#ifdef GAME_DLL
+ if ( m_hDeployedShield.Get() )
+ {
+ m_hDeployedShield.Get()->Remove( );
+ m_hDeployedShield.Set( NULL );
+ }
+#endif
+
+ m_bIsDeployed = false;
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponShield::ItemPostFrame( void )
+{
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ if (pPlayer == NULL)
+ return;
+
+ // Disabled
+ if ( ComputeEMPFireState() )
+ {
+ // Handle deployment & pick-up (firing is handled on the client)
+ if ( pPlayer->m_nButtons & IN_ATTACK2 )
+ {
+ if ( gpGlobals->curtime >= m_flNextPrimaryAttack)
+ {
+ PrimaryAttack();
+ }
+ }
+ }
+
+ WeaponIdle();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponShield::PrimaryAttack( void )
+{
+ CShield *shield = m_hDeployedShield.Get();
+ if ( shield )
+ {
+ SetShieldPositionLocked( !m_bShieldPositionLocked );
+
+#ifdef GAME_DLL
+ if ( m_bShieldPositionLocked )
+ {
+ ClientPrint( ToBasePlayer(GetOwner()), HUD_PRINTCENTER, "Projected Shield Locked" );
+ }
+ else
+ {
+ ClientPrint( ToBasePlayer(GetOwner()), HUD_PRINTCENTER, "Projected Shield Tracking" );
+ }
+#endif
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.25f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Lock the projected shield
+//-----------------------------------------------------------------------------
+void CWeaponShield::SetShieldPositionLocked( bool bLocked )
+{
+ m_bShieldPositionLocked = bLocked;
+ if ( m_hDeployedShield.Get() )
+ {
+ if ( bLocked && m_hDeployedShield.Get()->IsAlwaysOrienting() )
+ {
+ CBaseTFPlayer *pPlayer = ToBaseTFPlayer( GetOwner() );
+ m_hDeployedShield.Get()->SetCenterAngles( pPlayer->EyeAngles() );
+ }
+ m_hDeployedShield.Get()->SetAlwaysOrient( !bLocked );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Idle processing
+//-----------------------------------------------------------------------------
+void CWeaponShield::WeaponIdle( void )
+{
+ bool isEmped = IsOwnerEMPed();
+
+ if (m_hDeployedShield.Get())
+ {
+ m_hDeployedShield.Get()->SetEMPed( isEmped );
+ }
+}
diff --git a/game/shared/tf2/weapon_shield.h b/game/shared/tf2/weapon_shield.h
new file mode 100644
index 0000000..9d6bb21
--- /dev/null
+++ b/game/shared/tf2/weapon_shield.h
@@ -0,0 +1,83 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_SHIELD_H
+#define WEAPON_SHIELD_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//#include "tf_player.h"
+#include "basetfcombatweapon_shared.h"
+#include "tf_shieldshared.h"
+
+
+#if defined( CLIENT_DLL )
+#define CWeaponShield C_WeaponShield
+#define CShield C_Shield
+#endif
+
+class CShield;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+class CWeaponShield : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponShield, CBaseTFCombatWeapon );
+
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponShield();
+
+ virtual void UpdateOnRemove( void );
+
+ // Firing
+ virtual void ItemPostFrame( void );
+ virtual float GetFireRate( void );
+ virtual float GetDamage( float flDistance, int iLocation );
+
+ virtual bool Deploy( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ virtual void WeaponIdle( void );
+ virtual bool VisibleInWeaponSelection( void ) { return false; }
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+
+private:
+ CWeaponShield( const CWeaponShield & );
+
+ // Lock the projected shield
+ void SetShieldPositionLocked( bool bLocked );
+
+ // Input
+ void PrimaryAttack( void );
+
+private:
+ // Data
+ CNetworkVar( CHandle<CShield>, m_hDeployedShield );
+ bool m_bShieldPositionLocked;
+ bool m_bIsDeployed;
+};
+
+#endif // WEAPON_SHIELD_H
diff --git a/game/shared/tf2/weapon_shieldgrenade.cpp b/game/shared/tf2/weapon_shieldgrenade.cpp
new file mode 100644
index 0000000..a06d1a5
--- /dev/null
+++ b/game/shared/tf2/weapon_shieldgrenade.cpp
@@ -0,0 +1,211 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_combat_usedwithshieldbase.h"
+#include "weapon_combat_basegrenade.h"
+#include "weapon_combatshield.h"
+#include "in_buttons.h"
+
+#if !defined( CLIENT_DLL )
+#include "tf_shieldgrenade.h"
+#else
+
+#define CWeaponShieldGrenade C_WeaponShieldGrenade
+
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#define SHIELD_GRENADE_LIFETIME 10.0f // 10 seconds
+
+//-----------------------------------------------------------------------------
+// The shield grenade weapon
+//-----------------------------------------------------------------------------
+
+class CWeaponShieldGrenade: public CWeaponCombatUsedWithShieldBase
+{
+ DECLARE_CLASS( CWeaponShieldGrenade, CWeaponCombatUsedWithShieldBase );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponShieldGrenade();
+
+ void Spawn( void );
+ void ItemPostFrame( void );
+ void PrimaryAttack( void );
+ void ThrowGrenade( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+ virtual bool ShouldPredict( void )
+ {
+ if ( GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+#endif
+private:
+ // A hack to work around missing animation feature
+ float m_flStartedThrowAt;
+
+private:
+ CWeaponShieldGrenade( const CWeaponShieldGrenade & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponShieldGrenade, DT_WeaponShieldGrenade )
+
+BEGIN_NETWORK_TABLE( CWeaponShieldGrenade, DT_WeaponShieldGrenade )
+END_NETWORK_TABLE()
+
+LINK_ENTITY_TO_CLASS( weapon_shield_grenade, CWeaponShieldGrenade );
+PRECACHE_WEAPON_REGISTER(weapon_shield_grenade);
+
+BEGIN_PREDICTION_DATA( CWeaponShieldGrenade )
+ DEFINE_FIELD( m_flStartedThrowAt, FIELD_FLOAT ),
+END_PREDICTION_DATA();
+
+CWeaponShieldGrenade::CWeaponShieldGrenade( void )
+{
+ SetPredictionEligible( true );
+}
+
+//-----------------------------------------------------------------------------
+// Yeehaw
+//-----------------------------------------------------------------------------
+void CWeaponShieldGrenade::Spawn( void )
+{
+ BaseClass::Spawn();
+ m_flStartedThrowAt = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponShieldGrenade::ItemPostFrame( void )
+{
+ CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
+ if (!pOwner)
+ return;
+
+ // Look for button downs
+ if ( (pOwner->m_nButtons & IN_ATTACK) && !m_flStartedThrowAt && (m_flNextPrimaryAttack <= gpGlobals->curtime) )
+ {
+ m_flStartedThrowAt = gpGlobals->curtime;
+
+ SendWeaponAnim( ACT_VM_DRAW );
+ }
+
+ // Look for button ups
+ if ( (pOwner->m_afButtonReleased & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) && m_flStartedThrowAt )
+ {
+ m_flNextPrimaryAttack = gpGlobals->curtime;
+ PrimaryAttack();
+ m_flStartedThrowAt = 0;
+ }
+
+ // No buttons down?
+ if ( !((pOwner->m_nButtons & IN_ATTACK) || (pOwner->m_nButtons & IN_ATTACK2) || (pOwner->m_nButtons & IN_RELOAD)) )
+ {
+ WeaponIdle( );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponShieldGrenade::PrimaryAttack( void )
+{
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ if ( !ComputeEMPFireState() )
+ return;
+
+ if ( !GetPrimaryAmmo() )
+ return;
+
+ // player "shoot" animation
+ PlayAttackAnimation( ACT_VM_THROW );
+
+ ThrowGrenade();
+
+ // Setup for refire
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+ CheckRemoveDisguise();
+
+ // If I'm now out of ammo, switch away
+ if ( !HasPrimaryAmmo() )
+ {
+ g_pGameRules->GetNextBestWeapon( pPlayer, NULL );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponShieldGrenade::ThrowGrenade( void )
+{
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer*>( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ BaseClass::WeaponSound(WPN_DOUBLE);
+
+ // Calculate launch velocity (3 seconds for max distance)
+ float flThrowTime = MIN( (gpGlobals->curtime - m_flStartedThrowAt), 3.0 );
+ float flSpeed = 800 + (200 * flThrowTime);
+
+ // If the player's crouched, roll the grenade
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ // Launch the grenade
+ Vector vecForward;
+ QAngle vecAngles = pPlayer->EyeAngles();
+ // Throw it up just a tad
+ vecAngles.x = -1;
+ AngleVectors( vecAngles, &vecForward, NULL, NULL);
+ Vector vecOrigin;
+ VectorLerp( pPlayer->EyePosition(), pPlayer->GetAbsOrigin(), 0.25f, vecOrigin );
+ vecOrigin += (vecForward * 16);
+ vecForward = vecForward * flSpeed;
+
+ QAngle vecGrenAngles;
+ vecGrenAngles.Init( 0, vecAngles.y, 0 );
+#if !defined( CLIENT_DLL )
+ CreateShieldGrenade( vecOrigin, vecGrenAngles, vecForward, vec3_angle, pPlayer, SHIELD_GRENADE_LIFETIME );
+#endif
+ }
+ else
+ {
+ // Launch the grenade
+ Vector vecForward;
+ QAngle vecAngles = pPlayer->EyeAngles();
+ AngleVectors( vecAngles, &vecForward, NULL, NULL);
+ Vector vecOrigin = pPlayer->EyePosition();
+ vecOrigin += (vecForward * 16);
+ vecForward = vecForward * flSpeed;
+
+ QAngle vecGrenAngles;
+ vecGrenAngles.Init( 0, vecAngles.y, 0 );
+#if !defined( CLIENT_DLL )
+ CreateShieldGrenade( vecOrigin, vecGrenAngles, vecForward, vec3_angle, pPlayer, SHIELD_GRENADE_LIFETIME );
+#endif
+ }
+
+ pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
+}
diff --git a/game/shared/tf2/weapon_twohandedcontainer.cpp b/game/shared/tf2/weapon_twohandedcontainer.cpp
new file mode 100644
index 0000000..62bedaf
--- /dev/null
+++ b/game/shared/tf2/weapon_twohandedcontainer.cpp
@@ -0,0 +1,395 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Support weapon and weapons contained within it
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetfplayer_shared.h"
+#include "weapon_twohandedcontainer.h"
+#include "baseviewmodel_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponTwoHandedContainer::CWeaponTwoHandedContainer()
+{
+ m_hRightWeapon = NULL;
+ m_hLeftWeapon = NULL;
+ SetPredictionEligible( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CWeaponTwoHandedContainer::~CWeaponTwoHandedContainer()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponTwoHandedContainer::Spawn( void )
+{
+ BaseClass::Spawn();
+}
+
+#ifdef CLIENT_DLL
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponTwoHandedContainer::GetViewmodelBoneControllers( CBaseViewModel *pViewModel, float controllers[MAXSTUDIOBONECTRLS])
+{
+ C_BasePlayer *player = ToBasePlayer( GetOwner() );
+ Assert( player );
+
+ if ( !player )
+ return;
+
+ // Find the weapon that matches the viewmodel
+ if ( m_hLeftWeapon != NULL && player->GetViewModel(0) == pViewModel )
+ {
+ m_hLeftWeapon->GetViewmodelBoneControllers( pViewModel, controllers);
+ }
+ else if ( m_hRightWeapon != NULL && player->GetViewModel(1) == pViewModel )
+ {
+ m_hRightWeapon->GetViewmodelBoneControllers( pViewModel, controllers);
+ }
+}
+
+#else // CLIENT_DLL
+
+void CWeaponTwoHandedContainer::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
+{
+ // Skip this work if we're already marked for transmission.
+ if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
+ return;
+
+ // Send our left and right weapons.
+ if ( m_hLeftWeapon )
+ m_hLeftWeapon->SetTransmit( pInfo, bAlways );
+
+ if ( m_hRightWeapon )
+ m_hRightWeapon->SetTransmit( pInfo, bAlways );
+
+ BaseClass::SetTransmit( pInfo, bAlways );
+}
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CWeaponTwoHandedContainer::GetViewModel( int viewmodelindex /*=0*/ )
+{
+ if ( m_hLeftWeapon != NULL && m_hRightWeapon != NULL )
+ {
+ if ( viewmodelindex == 0 )
+ {
+ return m_hLeftWeapon->GetViewModel();
+ }
+ else
+ {
+ return m_hRightWeapon->GetViewModel();
+ }
+ }
+ return BaseClass::GetViewModel( viewmodelindex );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the string to print death notices with
+//-----------------------------------------------------------------------------
+char *CWeaponTwoHandedContainer::GetDeathNoticeName( void )
+{
+ // If we have a weapon in our left slot, return it. Otherwise, return this weapon.
+ if ( m_hLeftWeapon )
+ return m_hLeftWeapon->GetDeathNoticeName();
+
+ return BaseClass::GetDeathNoticeName();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponTwoHandedContainer::ItemPostFrame( void )
+{
+ // HACK HACK: Do nonshield first in case it disallows ItemPostFrame on shield
+ if ( m_hLeftWeapon != NULL )
+ {
+// REMOVE WHEN ALL WEAPONS ARE PREDICTED!
+#if defined( CLIENT_DLL )
+ if ( m_hLeftWeapon->IsPredicted() )
+#endif
+ m_hLeftWeapon->ItemPostFrame();
+ }
+
+ if ( m_hRightWeapon != NULL && m_hRightWeapon->IsPredicted() )
+ {
+// REMOVE WHEN ALL WEAPONS ARE PREDICTED!
+#if defined( CLIENT_DLL )
+ if ( m_hRightWeapon->IsPredicted() )
+#endif
+ m_hRightWeapon->ItemPostFrame();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called each frame by the player PostThink, if the player's not ready to attack yet
+//-----------------------------------------------------------------------------
+void CWeaponTwoHandedContainer::ItemBusyFrame( void )
+{
+ // HACK HACK: Do nonshield first in case it disallows ItemPostFrame on shield
+ if ( m_hLeftWeapon != NULL )
+ {
+ m_hLeftWeapon->ItemBusyFrame();
+ }
+
+ if ( m_hRightWeapon != NULL )
+ {
+ m_hRightWeapon->ItemBusyFrame();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CWeaponTwoHandedContainer::SetWeapons( CBaseTFCombatWeapon *left, CBaseTFCombatWeapon *right )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( !pOwner )
+ return;
+
+ // Do we have a different left weapon?
+ if ( m_hLeftWeapon.Get() && m_hLeftWeapon != left )
+ {
+ // Holster our old one
+ m_hLeftWeapon->Holster();
+ m_hLeftWeapon = NULL;
+ }
+ // Do we have a different right weapon?
+ if ( m_hRightWeapon.Get() && m_hRightWeapon != right )
+ {
+ // Holster our old one
+ m_hRightWeapon->Holster();
+ m_hRightWeapon = NULL;
+ }
+
+ // Make new weapons if we need to
+ if ( !m_hLeftWeapon )
+ {
+ m_hLeftWeapon = left;
+ if ( m_hLeftWeapon )
+ {
+ m_hLeftWeapon->SetOwner( pOwner );
+ m_hLeftWeapon->Deploy();
+ m_hLeftWeapon->SetViewModelIndex( 0 );
+ //m_hLeftWeapon->SendWeaponAnim( ACT_IDLE );
+ }
+ }
+
+ if ( !m_hRightWeapon )
+ {
+ m_hRightWeapon = right;
+ if ( m_hRightWeapon )
+ {
+ m_hRightWeapon->SetOwner( pOwner );
+ m_hRightWeapon->Deploy();
+ m_hRightWeapon->SetViewModelIndex( 1 );
+ //m_hRightWeapon->SendWeaponAnim( ACT_IDLE );
+
+ UnhideSecondViewmodel();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Unhide the second viewmodel, in case we're switching from a single weapon
+//-----------------------------------------------------------------------------
+void CWeaponTwoHandedContainer::UnhideSecondViewmodel( void )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+ if ( pOwner )
+ {
+ CBaseViewModel *pVM = pOwner->GetViewModel(1);
+ if ( pVM )
+ {
+ pVM->RemoveEffects( EF_NODRAW );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Abort any reload we have in progress
+//-----------------------------------------------------------------------------
+void CWeaponTwoHandedContainer::AbortReload( void )
+{
+ BaseClass::AbortReload();
+
+ if ( m_hLeftWeapon )
+ {
+ m_hLeftWeapon->AbortReload();
+ }
+ if ( m_hRightWeapon )
+ {
+ m_hRightWeapon->AbortReload();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the left weapon has any ammo
+//-----------------------------------------------------------------------------
+bool CWeaponTwoHandedContainer::HasAnyAmmo( void )
+{
+ if ( m_hLeftWeapon )
+ return m_hLeftWeapon->HasAnyAmmo();
+
+ return BaseClass::HasAnyAmmo();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Deploy and start thinking
+//-----------------------------------------------------------------------------
+bool CWeaponTwoHandedContainer::Deploy( void )
+{
+ if ( !BaseClass::Deploy() )
+ return false;
+
+ if ( m_hLeftWeapon )
+ {
+ m_hLeftWeapon->Deploy();
+ }
+ if ( m_hRightWeapon )
+ {
+ m_hRightWeapon->Deploy();
+ UnhideSecondViewmodel();
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseCombatWeapon *CWeaponTwoHandedContainer::GetLastWeapon( void )
+{
+ if ( m_hLeftWeapon )
+ return m_hLeftWeapon->GetLastWeapon();
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CWeaponTwoHandedContainer::GetDefaultAnimSpeed( void )
+{
+ if ( m_hLeftWeapon )
+ return m_hLeftWeapon->GetDefaultAnimSpeed();
+
+ return 1.0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop thinking and holster
+//-----------------------------------------------------------------------------
+bool CWeaponTwoHandedContainer::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ CBaseTFPlayer *pOwner = ToBaseTFPlayer( GetOwner() );
+
+ // If I'm holstering a weapon for another weapon that supports two-handed, just switch them out
+ CBaseTFCombatWeapon *pWeapon = (CBaseTFCombatWeapon *)pSwitchingTo;
+ if ( pWeapon && pWeapon->SupportsTwoHanded() )
+ {
+ // For now, holster the left weapon and switch it.
+ // In the future, we might want weapons to say which side they'd like to be on
+ SetWeapons( pWeapon, m_hRightWeapon );
+
+ // We might need to force the new weapon to be in the right animation
+ if ( 0 ) //if ( m_hRightWeapon.Get() && m_hRightWeapon->IsReflectingAnimations() )
+ {
+ pWeapon->SendWeaponAnim( m_hRightWeapon->GetLastReflectedActivity() );
+ }
+
+ UnhideSecondViewmodel();
+ return false;
+ }
+
+ if ( m_hLeftWeapon )
+ {
+ m_hLeftWeapon->Holster(pSwitchingTo);
+ }
+ if ( m_hRightWeapon )
+ {
+ m_hRightWeapon->Holster(pSwitchingTo);
+ }
+
+ // We're changing to a single weapon, so hide the second viewmodel
+ if ( pOwner )
+ {
+ CBaseViewModel *pVM = pOwner->GetViewModel(1);
+ if ( pVM )
+ {
+ pVM->AddEffects( EF_NODRAW );
+ }
+ }
+
+ return BaseClass::Holster(pSwitchingTo);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Get the correct weight of our active weapon
+//-----------------------------------------------------------------------------
+int CWeaponTwoHandedContainer::GetWeight( void )
+{
+ if ( !m_hLeftWeapon )
+ return BaseClass::GetWeight();
+
+ return m_hLeftWeapon->GetWpnData().iWeight;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CBaseTFCombatWeapon
+//-----------------------------------------------------------------------------
+CBaseTFCombatWeapon *CWeaponTwoHandedContainer::GetLeftWeapon( void )
+{
+ return m_hLeftWeapon;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CBaseTFCombatWeapon
+//-----------------------------------------------------------------------------
+CBaseTFCombatWeapon *CWeaponTwoHandedContainer::GetRightWeapon( void )
+{
+ return m_hRightWeapon;
+}
+
+LINK_ENTITY_TO_CLASS( weapon_twohandedcontainer, CWeaponTwoHandedContainer );
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponTwoHandedContainer , DT_WeaponTwoHandedContainer )
+BEGIN_NETWORK_TABLE( CWeaponTwoHandedContainer , DT_WeaponTwoHandedContainer )
+#if !defined( CLIENT_DLL )
+ SendPropEHandle( SENDINFO(m_hRightWeapon) ),
+ SendPropEHandle( SENDINFO(m_hLeftWeapon) ),
+#else
+ RecvPropEHandle( RECVINFO(m_hRightWeapon ) ),
+ RecvPropEHandle( RECVINFO(m_hLeftWeapon ) ),
+#endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponTwoHandedContainer )
+
+ DEFINE_PRED_FIELD( m_hRightWeapon, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_hLeftWeapon, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+#if defined( CLIENT_DLL )
+ // DEFINE_FIELD( m_hOldRightWeapon, FIELD_EHANDLE ),
+ // DEFINE_FIELD( m_hOldLeftWeapon, FIELD_EHANDLE ),
+#endif
+
+END_PREDICTION_DATA()
+
+PRECACHE_WEAPON_REGISTER(weapon_twohandedcontainer);
diff --git a/game/shared/tf2/weapon_twohandedcontainer.h b/game/shared/tf2/weapon_twohandedcontainer.h
new file mode 100644
index 0000000..04cd942
--- /dev/null
+++ b/game/shared/tf2/weapon_twohandedcontainer.h
@@ -0,0 +1,97 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_TWOHANDEDCONTAINER_H
+#define WEAPON_TWOHANDEDCONTAINER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basetfcombatweapon_shared.h"
+#include "baseviewmodel_shared.h"
+#include "studio.h"
+
+#if defined( CLIENT_DLL )
+#define CWeaponTwoHandedContainer C_WeaponTwoHandedContainer
+#endif
+
+class CBaseViewModel;
+
+//-----------------------------------------------------------------------------
+// Purpose: Client side rep of CBaseTFCombatWeapon
+//-----------------------------------------------------------------------------
+class CWeaponTwoHandedContainer : public CBaseTFCombatWeapon
+{
+ DECLARE_CLASS( CWeaponTwoHandedContainer, CBaseTFCombatWeapon );
+public:
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponTwoHandedContainer();
+ ~CWeaponTwoHandedContainer();
+
+ virtual void Spawn( void );
+ virtual void ItemPostFrame( void );
+ virtual void ItemBusyFrame( void );
+ virtual void SetWeapons( CBaseTFCombatWeapon *left, CBaseTFCombatWeapon *right );
+ virtual void UnhideSecondViewmodel( void );
+ virtual void AbortReload( void );
+ virtual bool HasAnyAmmo( void );
+ virtual bool Deploy( void );
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
+ virtual CBaseCombatWeapon *GetLastWeapon( void );
+ virtual int GetWeight( void );
+
+ virtual const char *GetViewModel( int viewmodelindex = 0 );
+ virtual char *GetDeathNoticeName( void );
+ virtual float GetDefaultAnimSpeed( void );
+
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted( void ) const
+ {
+ return true;
+ }
+
+#if defined( CLIENT_DLL )
+
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+ virtual void GetViewmodelBoneControllers( CBaseViewModel *pViewModel, float controllers[MAXSTUDIOBONECTRLS]);
+
+ virtual void OnDataChanged( DataUpdateType_t updateType );
+
+ virtual bool ShouldDraw( void );
+ virtual void Redraw(void);
+ virtual void DrawAmmo( void );
+ virtual void HandleInput( void );
+ virtual void OverrideMouseInput( float *x, float *y );
+ virtual void ViewModelDrawn( CBaseViewModel *pBaseViewModel );
+ void HookWeaponEntities( void );
+ virtual bool VisibleInWeaponSelection( void );
+
+private:
+ CWeaponTwoHandedContainer( const CWeaponTwoHandedContainer & );
+public:
+
+#else
+
+ virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
+
+#endif
+
+ CBaseTFCombatWeapon *GetLeftWeapon( void );
+ CBaseTFCombatWeapon *GetRightWeapon( void );
+
+public:
+ CNetworkHandle( CBaseTFCombatWeapon, m_hRightWeapon );
+ CNetworkHandle( CBaseTFCombatWeapon, m_hLeftWeapon );
+#if defined( CLIENT_DLL )
+ CHandle<CBaseTFCombatWeapon> m_hOldRightWeapon;
+ CHandle<CBaseTFCombatWeapon> m_hOldLeftWeapon;
+#endif
+};
+
+#endif // WEAPON_TWOHANDEDCONTAINER_H