Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

487 lines
14 KiB

//========= Copyright © 1996-2009, Valve Corporation, All rights reserved. ============//
//
// Purpose: Shared variables, etc. for the paint gun.
//
//=============================================================================//
#include "cbase.h"
#include "paint_color_manager.h"
#include "shot_manipulator.h"
#include "in_buttons.h"
#include "debugoverlay_shared.h"
#include "weapon_paintgun_shared.h"
#include "paint_sprayer_shared.h"
#include "paint_stream_shared.h"
#ifdef CLIENT_DLL
#include "c_weapon_paintgun.h"
#include "c_portal_player.h"
#include "igameevents.h"
#else
#include "weapon_paintgun.h"
#include "portal_player.h"
#include "paint_database.h"
#include "portal_base2d.h"
#include "prop_portal_shared.h"
#include "env_speaker.h"
#include "rumble_shared.h"
#include "paint_database.h"
//ConVar sv_paint_erase_range("sv_paint_erase_range", "2000", FCVAR_CHEAT);
//ConVar sv_num_erase_ray("sv_num_erase_ray", "10", FCVAR_CHEAT, "number of ray that shoots out per shot");
//ConVar sv_debug_suck_erase("sv_debug_suck_erase", "0", FCVAR_CHEAT);
extern void Paint( CBaseEntity* pEntity, const Vector& pos, uint8 colorIndex, int nPainted );
extern CPaintDatabase PaintDatabase;
#endif
#define paintgun_blobs_spread_radius 0.f //ConVar paintgun_blobs_spread_radius( "paintgun_blobs_spread_radius", "0.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The starting radius of the spread of the paint blobs from the gun" );
#define paintgun_blobs_spread_angle 10.f //ConVar paintgun_blobs_spread_angle( "paintgun_blobs_spread_angle", "10.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The spread (in degrees) of the paint blobs from the gun" );
#define paintgun_blobs_per_second 40.f //ConVar paintgun_blobs_per_second( "paintgun_blobs_per_second", "40.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "Number of blobs shot out of the paint gun per second" );
#define paintgun_blobs_min_speed 950.f //ConVar paintgun_blobs_min_speed( "paintgun_blobs_min_speed", "950.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The min speed of the blobs shot out of the paint gun" );
#define paintgun_blobs_max_speed 1050.f //ConVar paintgun_blobs_max_speed( "paintgun_blobs_max_speed", "1050.0f", FCVAR_REPLICATED | FCVAR_CHEAT, "The max speed of the blobs shot out of the paint gun" );
#define paintgun_shoot_position_trace_for_wall 1 //ConVar paintgun_shoot_position_trace_for_wall( "paintgun_shoot_position_trace_for_wall", "1", FCVAR_REPLICATED, "If the paint gun shooting position should test if it is inside a wall" );
#define paintgun_blobs_streak_percent 10.f //ConVar paintgun_blobs_streak_percent( "paintgun_blobs_streak_percent", "10.0f", FCVAR_REPLICATED | FCVAR_CHEAT );
#define paintgun_blobs_min_streak_time 0.1f //ConVar paintgun_blobs_min_streak_time( "paintgun_blobs_min_streak_time", "0.1f", FCVAR_REPLICATED | FCVAR_CHEAT );
#define paintgun_blobs_max_streak_time 0.5f //ConVar paintgun_blobs_max_streak_time( "paintgun_blobs_max_streak_time", "0.5f", FCVAR_REPLICATED | FCVAR_CHEAT );
#define paintgun_blobs_min_streak_speed_dampen 4500.f //ConVar paintgun_blobs_min_streak_speed_dampen( "paintgun_blobs_min_streak_speed_dampen", "4500.0f", FCVAR_REPLICATED | FCVAR_CHEAT );
#define paintgun_blobs_max_streak_speed_dampen 5500.f //ConVar paintgun_blobs_max_streak_speed_dampen( "paintgun_blobs_max_streak_speed_dampen", "5500.0f", FCVAR_REPLICATED | FCVAR_CHEAT );
#define paintgun_max_ammo 60 //ConVar paintgun_max_ammo( "paintgun_max_ammo", "60", FCVAR_REPLICATED, "The maximum amount of paint ammo allowed." );
#define paintgun_ammo_type 0 //ConVar paintgun_ammo_type( "paintgun_ammo_type", "0", FCVAR_REPLICATED, "Type of paint ammo. 0: No ammo, 1: Global ammo per-gun, 2: Ammo per-paint type" );
acttable_t CWeaponPaintGun::m_acttable[] =
{
{ ACT_MP_STAND_IDLE, ACT_MP_STAND_PRIMARY, false },
{ ACT_MP_RUN, ACT_MP_RUN_PRIMARY, false },
{ ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_PRIMARY, false },
{ ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_PRIMARY, false },
{ ACT_MP_JUMP_START, ACT_MP_JUMP_START_PRIMARY, false },
{ ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_PRIMARY, false },
{ ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_PRIMARY, false },
{ ACT_MP_AIRWALK, ACT_MP_AIRWALK_PRIMARY, false },
{ ACT_MP_RUN_SPEEDPAINT, ACT_MP_RUN_SPEEDPAINT_PRIMARY, false },
{ ACT_MP_DROWNING_PRIMARY, ACT_MP_DROWNING_PRIMARY, false },
{ ACT_MP_LONG_FALL, ACT_MP_LONG_FALL_PRIMARY, false },
{ ACT_MP_TRACTORBEAM_FLOAT, ACT_MP_TRACTORBEAM_FLOAT_PRIMARY, false },
{ ACT_MP_DEATH_CRUSH, ACT_MP_DEATH_CRUSH_PRIMARY, false },
};
IMPLEMENT_ACTTABLE(CWeaponPaintGun);
void CWeaponPaintGun::ItemPostFrame()
{
bool bWasFiringPaint = m_bFiringPaint;
bool bWasFiringErase = m_bFiringErase;
// Only the player fires this way so we can cast
CPortal_Player *pPlayer = ToPortalPlayer( GetOwner() );
if ( pPlayer == NULL )
return;
// The paint clearing secondary function can always be used
if( paintgun_ammo_type != PAINT_AMMO_NONE &&
(pPlayer->m_nButtons & IN_ATTACK2) != 0 )
{
// Attack!
SecondaryAttack();
}
else if( pPlayer->GetUseEntity() == NULL )
{
BaseClass::ItemPostFrame();
}
// Was shooting neither and is now shooting either
if( !bWasFiringPaint && !bWasFiringErase &&
( m_bFiringPaint || m_bFiringErase ) )
{
#if !defined (CLIENT_DLL)
StartShootingSound();
#else
pPlayer->SetAnimation( PLAYER_ATTACK1 );
#endif
}
// Was shooting either and now is shooting neither
else if( ( bWasFiringPaint || bWasFiringErase ) &&
( !m_bFiringPaint && !m_bFiringErase ) )
{
#if !defined (CLIENT_DLL)
StopShootingSound();
#else
#endif
}
}
void CWeaponPaintGun::PrimaryAttack()
{
bool bHasSelectedColor = false;
#if !defined (CLIENT_DLL)
bHasSelectedColor = HasPaintPower( (PaintPowerType)m_nCurrentColor.Get() );
#else // CLIENT_DLL
bHasSelectedColor = HasPaintPower( (PaintPowerType)m_nCurrentColor );
#endif
// Don't shoot if we dont have the selected color or any color at all
if( !HasAnyPaintPower() || !bHasSelectedColor || !HasPaintAmmo( m_nCurrentColor ) )
{
m_bFiringPaint = m_bFiringErase = false;
return;
}
#if !defined (CLIENT_DLL)
SprayPaint( gpGlobals->frametime, m_nCurrentColor );
if( !m_bFiringPaint )
{
IGameEvent *event = gameeventmanager->CreateEvent( "player_painted" );
if ( event )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
assert( pPlayer );
event->SetInt("userid", pPlayer->GetUserID() );
gameeventmanager->FireEvent( event );
}
}
#else // CLIENT_DLL
StartHoseEffect();
//SprayPaint( gpGlobals->frametime, static_cast<PaintPowerType>( m_nCurrentColor ) );
#endif //CLIENT_DLL
m_bFiringPaint = true;
m_bFiringErase = false;
}
void CWeaponPaintGun::SecondaryAttack()
{
if( paintgun_ammo_type == PAINT_AMMO_NONE )
{
# ifdef CLIENT_DLL
StartHoseEffect();
# else
if( !m_bFiringErase )
{
IGameEvent *event = gameeventmanager->CreateEvent( "player_erased" );
if ( event )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
assert( pPlayer );
event->SetInt("userid", pPlayer->GetUserID() );
gameeventmanager->FireEvent( event );
}
}
m_bFiringPaint = false;
m_bFiringErase = true;
SprayPaint( gpGlobals->frametime, NO_POWER );
# endif
}
else
{
ResetAmmo();
#ifdef GAME_DLL
PaintDatabase.RemoveAllPaint();
#endif
}
}
bool CWeaponPaintGun::HasPaintPower( PaintPowerType nIndex )
{
return m_bHasPaint[nIndex];
}
bool CWeaponPaintGun::HasAnyPaintPower()
{
for( int i = 0; i < PAINT_POWER_TYPE_COUNT; ++i )
{
if( HasPaintPower( (PaintPowerType)i ) )
{
return true;
}
}
return false;
}
void CWeaponPaintGun::WeaponIdle()
{
#ifdef CLIENT_DLL
StopHoseEffect();
#else
if( m_bFiringPaint || m_bFiringErase )
{
StopShootingSound();
}
#endif
m_bFiringPaint = m_bFiringErase = false;
m_flAccumulatedTime = 1.0f/paintgun_blobs_per_second;
#ifdef CLIENT_DLL
#endif
m_nBlobRandomSeed = 0;
BaseClass::WeaponIdle();
}
bool CWeaponPaintGun::Holster( CBaseCombatWeapon *pSwitchingTo )
{
m_bFiringPaint = m_bFiringErase = false;
#ifdef CLIENT_DLL
ChangeRenderColor();
StopHoseEffect();
#else
StopShootingSound();
IGameEvent *event = gameeventmanager->CreateEvent( "holstered_paintgun" );
if ( event )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if( pPlayer )
{
event->SetInt("userid", pPlayer->GetUserID() );
gameeventmanager->FireEvent( event );
}
}
#endif
return BaseClass::Holster( pSwitchingTo );
}
void CWeaponPaintGun::Drop( const Vector &vecVelocity )
{
m_bFiringPaint = m_bFiringErase = false;
Color color = MapPowerToVisualColor( m_nCurrentColor );
if ( !HasAnyPaintPower() )
color = MapPowerToVisualColor( NO_POWER );
#ifdef CLIENT_DLL
StopHoseEffect();
#else
StopShootingSound();
IGameEvent *event = gameeventmanager->CreateEvent( "dropped_paintgun" );
if ( event )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if( pPlayer )
{
event->SetInt("userid", pPlayer->GetUserID() );
gameeventmanager->FireEvent( event );
}
}
#endif
SetRenderColor( color.r(), color.g(), color.b() );
BaseClass::Drop( vecVelocity );
}
bool CWeaponPaintGun::Deploy()
{
#ifdef CLIENT_DLL
ChangeRenderColor();
#endif
#ifndef CLIENT_DLL
IGameEvent *event = gameeventmanager->CreateEvent( "deployed_paintgun" );
if ( event )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if( pPlayer )
{
event->SetInt("userid", pPlayer->GetUserID() );
event->SetInt("paintcount", GetPaintCount() );
gameeventmanager->FireEvent( event );
}
}
#endif //Only on server
return BaseClass::Deploy();
}
void CWeaponPaintGun::SetSubType( int iType )
{
m_iSubType = iType;
m_nCurrentColor = iType;
#ifdef CLIENT_DLL
ChangeRenderColor();
#else
EmitSound( "Player.WeaponSelected" );
#endif
BaseClass::SetSubType( iType );
}
PaintPowerType CWeaponPaintGun::GetCurrentPaint()
{
#ifdef CLIENT_DLL
return (PaintPowerType)( m_nCurrentColor );
#else //!CLIENT_DLL
return (PaintPowerType)( m_nCurrentColor.Get() );
#endif
}
//Paint Ammo!
bool CWeaponPaintGun::HasPaintAmmo( unsigned paintType ) const
{
switch( paintgun_ammo_type )
{
case PAINT_AMMO_NONE:
return true;
case PAINT_AMMO_GLOBAL:
return m_nPaintAmmo > 0;
case PAINT_AMMO_PER_TYPE:
Assert( paintType < PAINT_POWER_TYPE_COUNT );
return m_PaintAmmoPerType[MIN( paintType, PAINT_POWER_TYPE_COUNT )] > 0;
default:
return true;
}
}
void CWeaponPaintGun::DecrementPaintAmmo( unsigned paintType )
{
switch( paintgun_ammo_type )
{
case PAINT_AMMO_GLOBAL:
--m_nPaintAmmo;
break;
case PAINT_AMMO_PER_TYPE:
const int index = MIN( paintType, PAINT_POWER_TYPE_COUNT );
m_PaintAmmoPerType.Set( index, m_PaintAmmoPerType[index] - 1 );
break;
}
}
void CWeaponPaintGun::ResetAmmo()
{
m_nPaintAmmo = paintgun_max_ammo;
const int maxAmmo = paintgun_max_ammo;
for( int i = 0; i < PAINT_POWER_TYPE_COUNT; ++i )
{
m_PaintAmmoPerType.Set( i, maxAmmo );
}
}
void CWeaponPaintGun::SprayPaint( float flDeltaTime, int paintType )
{
if( flDeltaTime <= 0.0f )
{
return;
}
CPortal_Player *pOwner = ToPortalPlayer( GetOwner() );
if ( pOwner == NULL )
return;
CPaintStream *pPaintStream = assert_cast< CPaintStream* >( m_hPaintStream.Get( paintType ).Get() );
if ( !pPaintStream )
return;
m_flAccumulatedTime += flDeltaTime;
Vector vecEyePosition = pOwner->EyePosition();
Vector vecVelocity = pOwner->GetAbsVelocity();
Vector vecAimDir = pOwner->GetAutoaimVector( 0 );
Vector vecForwardVelocity = vecVelocity.Normalized() * DotProduct( vecVelocity, vecAimDir );
Vector vecBlobFirePos = pOwner->GetPaintGunShootPosition();
if( paintgun_shoot_position_trace_for_wall )
{
// Because the muzzle is so long, it can stick through a wall if the player is right up against it.
// Make sure to adjust the shoot position in this condition by tracing a line between the eye point and the end of the muzzle.
trace_t trace;
Ray_t muzzleRay;
muzzleRay.Init( vecEyePosition, vecBlobFirePos );
CTraceFilterSimple traceFilter( pOwner, COLLISION_GROUP_NONE );
UTIL_TraceRay( muzzleRay, MASK_SOLID, &traceFilter, &trace );
//Check if there is a portal between the player's eye and the muzzle of the paint gun
CPortal_Base2D *pInPortal = NULL;
CPortal_Base2D *pOutPortal = NULL;
if( UTIL_DidTraceTouchPortals( muzzleRay, trace, &pInPortal, &pOutPortal ) )
{
Vector vecPortalForward;
AngleVectors( pInPortal->GetAbsAngles(), &vecPortalForward );
Vector vecTraceDir = vecBlobFirePos - vecEyePosition;
vecTraceDir.NormalizeInPlace();
if( DotProduct( vecPortalForward, vecTraceDir ) < 0 )
{
UTIL_Portal_PointTransform( pInPortal->MatrixThisToLinked(), trace.endpos, vecBlobFirePos );
UTIL_Portal_VectorTransform( pInPortal->MatrixThisToLinked(), vecAimDir, vecAimDir );
}
}
else if ( trace.fraction < 1.0 && ( !trace.m_pEnt || trace.m_pEnt->m_takedamage == DAMAGE_NO ) )
{
// there is something between the eye and the end of the muzzle, most likely a wall
// Move the muzzle position to the end position of the trace so that the wall gets painted
vecBlobFirePos = trace.endpos;
}
//vecBlobFirePos = trace.endpos;
}
const float flBlobPerSecond = 1.0f/paintgun_blobs_per_second;
while ( m_flAccumulatedTime >= flBlobPerSecond && HasPaintAmmo( paintType ) )
{
m_flAccumulatedTime -= flBlobPerSecond;
CPaintBlob *pBlob = FirePaintBlob( vecBlobFirePos,
vecBlobFirePos,
vecForwardVelocity,
vecAimDir,
paintType,
paintgun_blobs_spread_radius,
paintgun_blobs_spread_angle,
paintgun_blobs_min_speed,
paintgun_blobs_max_speed,
paintgun_blobs_streak_percent,
paintgun_blobs_min_streak_time,
paintgun_blobs_max_streak_time,
paintgun_blobs_min_streak_speed_dampen,
paintgun_blobs_max_streak_speed_dampen,
false,
false,
pPaintStream,
m_nBlobRandomSeed );
pPaintStream->AddPaintBlob( pBlob );
++m_nBlobRandomSeed;
DecrementPaintAmmo( paintType );
}
}