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
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 );
|
|
}
|
|
}
|