diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/tf2 | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/shared/tf2')
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 |