Гаусс

Материал из Наша Wiki - Наша энциклопедия Half-Life
Перейти к: навигация, поиск

Сегодня мы займёмся созданием гауссовой винтовки :) Иными словами Гауссовки :) для оружия применим более рациональное название Tau Cannon.

ВНИМАНИЕ : модель к туториалу прилогаться не будет :)

Итак, создадим tau_cannon.h чтобы он был в moddir/src/dlls/hl2_dll И туда отпостим :

//========= Copyright © 2002-2008, Lolmen & Valve, All rights reserved. ============
//
// Purpose: Tau Cannon Super gun
//
//==================================================================================

#include "basehlcombatweapon.h"

#ifndef WEAPON_GAUSS_H
#define WEAPON_GAUSS_H
#ifdef _WIN32
#pragma once
#endif

#include "te_particlesystem.h"
#include "effect_dispatch_data.h"

#define GAUSS_BEAM_SPRITE		"sprites/laserbeam.vmt"

#define	GAUSS_CHARGE_TIME		0.2f
#define	MAX_GAUSS_CHARGE		16
#define	MAX_GAUSS_CHARGE_TIME		3
#define	DANGER_GAUSS_CHARGE_TIME	10


//=============================================================================
// Tau cannon
//=============================================================================

class CWeaponTauCannon : public CBaseHLCombatWeapon
{
	DECLARE_DATADESC();
public:
	DECLARE_CLASS( CWeaponTauCannon, CBaseHLCombatWeapon );

	CWeaponTauCannon( void ); // Конструктор

	DECLARE_SERVERCLASS();

	void	Spawn( void );
	void	Precache( void );
	void	PrimaryAttack( void );
	void	SecondaryAttack( void );
	void	AddViewKick( void );

	bool	Holster( CBaseCombatWeapon *pSwitchingTo = NULL );

	void	ItemPostFrame( void );

	float	GetFireRate( void ) { return 0.2f; } // Скорострельность

	virtual const Vector &GetBulletSpread( void ) // Конус разброса
	{
		static Vector cone = VECTOR_CONE_1DEGREES;	
		return cone;
	}

protected:

	void	Fire( void );
	void	ChargedFire( void );
	float	GetFullChargeTime( void );
	void	StartFire( void );
	void	StopChargeSound( void );

	void	DrawBeam( const Vector &startPos, const Vector &endPos, float width, bool useMuzzle = false );
	void	IncreaseCharge( void );
	bool	ShouldDrawWaterImpacts( const trace_t &shot_trace ); // Lolmen : Добавим брызг по воде

private:
	EHANDLE			m_hViewModel;
	float			m_flNextChargeTime;
	CSoundPatch		*m_sndCharge;

	float			m_flChargeStartTime;
	bool			m_bCharging;
	bool			m_bChargeIndicated;

	DECLARE_ACTTABLE();
};

#endif // WEAPON_GAUSS_H

Так, теперь идём опять в moddir/src/dlls/hl2_dll создаём там tau_cannon.cpp


//========= Copyright © 2002-2008, Lolmen & Valve, All rights reserved. ============
//
// Purpose: Tau Cannon Super gun
//
//==================================================================================

#include "cbase.h"
#include "player.h"
#include "gamerules.h"
#include "basehlcombatweapon.h"
#include "decals.h"
#include "beam_shared.h"
#include "AmmoDef.h"
#include "IEffects.h"
#include "engine/IEngineSound.h"
#include "in_buttons.h"
#include "soundenvelope.h"
#include "soundent.h"
#include "shake.h"
#include "explode.h"
#include "tau_cannon.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//-----------------------------------------------------------------------------
// Declarations
//-----------------------------------------------------------------------------

IMPLEMENT_SERVERCLASS_ST( CWeaponTauCannon, DT_WeaponTauCannon )
END_SEND_TABLE()

LINK_ENTITY_TO_CLASS( weapon_gauss, CWeaponTauCannon ); // обединения имени энити с этим классом
PRECACHE_WEAPON_REGISTER( weapon_gauss ); // имя тектового файла в scripts.txt

acttable_t	CWeaponTauCannon::m_acttable[] = 
{
	{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR2, true },
};

IMPLEMENT_ACTTABLE( CWeaponTauCannon );

//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CWeaponTauCannon )

	DEFINE_FIELD( m_hViewModel,		FIELD_EHANDLE ),
	DEFINE_FIELD( m_flNextChargeTime,	FIELD_TIME ),
	DEFINE_FIELD( m_flChargeStartTime,	FIELD_TIME ),
	DEFINE_FIELD( m_bCharging,		FIELD_BOOLEAN ),
	DEFINE_FIELD( m_bChargeIndicated,	FIELD_BOOLEAN ),

	DEFINE_SOUNDPATCH( m_sndCharge ),

END_DATADESC()


extern ConVar sk_plr_dmg_gauss; //  Ссылка на обьект
extern ConVar sk_plr_max_dmg_gauss; // Ссылка на обьект

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CWeaponTauCannon::CWeaponTauCannon( void )
{
	m_hViewModel 		= NULL;
	m_flNextChargeTime	= 0;
	m_flChargeStartTime	= 0;
	m_sndCharge		= NULL;
	m_bCharging		= false;
	m_bChargeIndicated	= false;
	m_bReloadsSingly	= false;
	m_bFiresUnderwater	= false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponTauCannon::Precache( void )
{
	enginesound->PrecacheSound( "weapons/gauss/chargeloop.wav" );
	BaseClass::Precache();
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponTauCannon::Spawn( void )
{
	BaseClass::Spawn();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponTauCannon::Fire( void )
{
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	
	if ( !pOwner ){ return; }

	m_bCharging = false;

	if ( m_hViewModel == NULL )
	{
		CBaseViewModel *vm = pOwner->GetViewModel();

		if ( vm )
		{
			m_hViewModel.Set( vm );
		}
	}

	Vector	startPos = pOwner->Weapon_ShootPosition();
	Vector	aimDir	 = pOwner->GetAutoaimVector( AUTOAIM_5DEGREES );

	Vector vecUp, vecRight;
	VectorVectors( aimDir, vecRight, vecUp );

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

	aimDir = aimDir + x * GetBulletSpread().x * vecRight + y * GetBulletSpread().y * vecUp;

	Vector	endPos	= startPos + ( aimDir * MAX_TRACE_LENGTH );
	
	// Тестируем дистанцию выстрела
	trace_t	tr;
	UTIL_TraceLine( startPos, endPos, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
	
	ClearMultiDamage();

	CBaseEntity *pHit = tr.m_pEnt;
	
	CTakeDamageInfo dmgInfo( this, pOwner, sk_plr_dmg_gauss.GetFloat(), DMG_SHOCK );

	if ( pHit != NULL )
	{
		CalculateBulletDamageForce( &dmgInfo, m_iPrimaryAmmoType, aimDir, tr.endpos );
		pHit->DispatchTraceAttack( dmgInfo, aimDir, &tr );
	}
	// Проверяем на наличие воды по пути луча
	ShouldDrawWaterImpacts( tr );

	if ( tr.DidHitWorld() ) // Рикошет
	{
		float hitAngle = -DotProduct( tr.plane.normal, aimDir );

		if ( hitAngle < 0.5f )
		{
			Vector vReflection;
		
			vReflection = 2.0 * tr.plane.normal * hitAngle + aimDir;
			
			startPos	= tr.endpos;
			endPos		= startPos + ( vReflection * MAX_TRACE_LENGTH );
			
			//Draw beam to reflection point
			DrawBeam( tr.startpos, tr.endpos, 1.6, true );

			CPVSFilter filter( tr.endpos );
			te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );

			UTIL_ImpactTrace( &tr, GetAmmoDef()->DamageType(m_iPrimaryAmmoType), "ImpactGauss" );

			//Find new reflection end position
			UTIL_TraceLine( startPos, endPos, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );

			if ( tr.m_pEnt != NULL )
			{
				dmgInfo.SetDamageForce( GetAmmoDef()->DamageForce(m_iPrimaryAmmoType) * vReflection );
				dmgInfo.SetDamagePosition( tr.endpos );
				tr.m_pEnt->DispatchTraceAttack( dmgInfo, vReflection, &tr );
			}

			//Connect reflection point to end
			DrawBeam( tr.startpos, tr.endpos, 0.4 );
		}
		else
		{
			DrawBeam( tr.startpos, tr.endpos, 1.6, true );
		}
	}
	else
	{
		DrawBeam( tr.startpos, tr.endpos, 1.6, true );
	}

	ApplyMultiDamage();

	UTIL_ImpactTrace( &tr, GetAmmoDef()->DamageType(m_iPrimaryAmmoType), "ImpactGauss" ); // след на стене

	CPVSFilter filter( tr.endpos );
	te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );

	m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;

	AddViewKick();

	// Регистрация всппышки света для AI
	pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
}

//-----------------------------------------------------------------------------
// Purpose: Накопительный выстрел
//-----------------------------------------------------------------------------
void CWeaponTauCannon::ChargedFire( void )
{
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	
	if ( !pOwner ){ return; }

	bool penetrated = false;

	// Играем шоковые выстрелы
	WeaponSound( SINGLE );
	WeaponSound( SPECIAL2 );

	SendWeaponAnim( ACT_VM_SECONDARYATTACK );
	StopChargeSound();

	m_bCharging = false;
	m_bChargeIndicated = false;

	m_flNextPrimaryAttack	= gpGlobals->curtime + 0.2f;
	m_flNextSecondaryAttack = gpGlobals->curtime + 0.5f;

	// Посмотрим откуда выйдет выстрел...
	Vector	startPos= pOwner->Weapon_ShootPosition();
	Vector	aimDir	= pOwner->GetAutoaimVector( AUTOAIM_5DEGREES );
	Vector	endPos	= startPos + ( aimDir * MAX_TRACE_LENGTH );
	
	trace_t	tr;
	UTIL_TraceLine( startPos, endPos, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
	ShouldDrawWaterImpacts( tr ); // Проверка на наличие воды
	ClearMultiDamage();

	// Насколько много урона надо нанести
	float flChargeAmount = ( gpGlobals->curtime - m_flChargeStartTime ) / MAX_GAUSS_CHARGE_TIME;

	// Скрепляем
	if ( flChargeAmount > 1.0f ){ flChargeAmount = 1.0f; }

	// Определение количества урона
	float flDamage = sk_plr_dmg_gauss.GetFloat() + ( ( sk_plr_max_dmg_gauss.GetFloat() - sk_plr_dmg_gauss.GetFloat() ) * flChargeAmount );

	CBaseEntity *pHit = tr.m_pEnt;
	if ( tr.DidHitWorld() )
	{
		// Пробуем пробить стену
		UTIL_ImpactTrace( &tr, GetAmmoDef()->DamageType(m_iPrimaryAmmoType), "ImpactGauss" );
		UTIL_DecalTrace( &tr, "RedGlowFade" );

		CPVSFilter filter( tr.endpos );
		te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
		Vector  vStore = tr.endpos;
		Vector	testPos = tr.endpos + ( aimDir * 48.0f );

		UTIL_TraceLine( testPos, tr.endpos, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );
			
		if ( !tr.allsolid )
		{ 
			UTIL_DecalTrace( &tr, "RedGlowFade" ); 
			penetrated = true;
			// И рисуем обратную деколь
			trace_t backward_tr;
			UTIL_TraceLine( tr.endpos, vStore, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &backward_tr );
			if ( backward_tr.DidHit() ){ 
				UTIL_ImpactTrace( &backward_tr, GetAmmoDef()->DamageType(m_iPrimaryAmmoType), "ImpactGauss" ); }

		}
	}
	else if ( pHit != NULL )
	{
		CTakeDamageInfo dmgInfo( this, pOwner, flDamage, DMG_SHOCK );
		CalculateBulletDamageForce( &dmgInfo, m_iPrimaryAmmoType, aimDir, tr.endpos );

		// Наносим прямой удар по всему что на пути
		pHit->DispatchTraceAttack( dmgInfo, aimDir, &tr );
	}

	ApplyMultiDamage();

	UTIL_ImpactTrace( &tr, GetAmmoDef()->DamageType(m_iPrimaryAmmoType), "ImpactGauss" );

	QAngle	viewPunch;

	viewPunch.x = random->RandomFloat( -4.0f, -8.0f );
	viewPunch.y = random->RandomFloat( -0.25f,  0.25f );
	viewPunch.z = 0;

	pOwner->ViewPunch( viewPunch ); // качаем экран игрока

	DrawBeam( startPos, tr.endpos, 9.6, true ); // рисуем луч
	// Подбросим игрока немного вверх? Такого в SP нету :D И всёже, Wall Jump? :D
	Vector	recoilForce = pOwner->GetAbsVelocity() - pOwner->GetAutoaimVector( 0 ) * ( flDamage * 5.0f );
	recoilForce[2] += 128.0f;	
	pOwner->SetAbsVelocity( recoilForce ); // отдача на тело игрока

	CPVSFilter filter( tr.endpos );
	te->GaussExplosion( filter, 0.0f, tr.endpos, tr.plane.normal, 0 );
	if ( penetrated )
	{
		// при проникновении через стену дамаг
		RadiusDamage( CTakeDamageInfo( this, this, flDamage, DMG_SHOCK ), tr.endpos, 200.0f, CLASS_NONE, NULL );

		// Выходим из стены и пошли искать дырку для деколи
		UTIL_TraceLine( tr.endpos, endPos, MASK_SHOT, pOwner, COLLISION_GROUP_NONE, &tr );

		UTIL_ImpactTrace( &tr, GetAmmoDef()->DamageType(m_iPrimaryAmmoType), "ImpactGauss" );
		UTIL_DecalTrace( &tr, "RedGlowFade" ); 
		// и после проникновение на кончике луча дамаг
		RadiusDamage( CTakeDamageInfo( this, this, flDamage, DMG_SHOCK ), tr.endpos, 200.0f, CLASS_NONE, NULL );
	}

	// Оповещаем AI о выстреле
	pOwner->SetMuzzleFlashTime( gpGlobals->curtime + 0.5 );
}

//-----------------------------------------------------------------------------
// Purpose: Рисуем лучик
//-----------------------------------------------------------------------------
void CWeaponTauCannon::DrawBeam( const Vector &startPos, const Vector &endPos, float width, bool useMuzzle )
{
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	
	if ( pOwner == NULL )
		return;

	//Check to store off our view model index
	if ( m_hViewModel == NULL )
	{
		CBaseViewModel *vm = pOwner->GetViewModel();

		if ( vm )
		{
			m_hViewModel.Set( vm );
		}
	}

	// Главный лучевой след
	CBeam *pBeam = CBeam::BeamCreate( GAUSS_BEAM_SPRITE, width );
	
	if ( useMuzzle )
	{
		pBeam->PointEntInit( endPos, m_hViewModel );
		pBeam->SetEndAttachment( 1 );
		pBeam->SetWidth( width / 4.0f );
		pBeam->SetEndWidth( width );
	}
	else
	{
		pBeam->SetStartPos( startPos );
		pBeam->SetEndPos( endPos );
		pBeam->SetWidth( width );
		pBeam->SetEndWidth( width / 4.0f );
	}

	pBeam->SetBrightness( 255 );
	pBeam->SetColor( 255, 145+random->RandomInt( -16, 16 ), 0 );
	pBeam->RelinkBeam();
	pBeam->LiveForTime( 0.1f );

	// Искры вокруг луча
	for ( int i = 0; i < 3; i++ )
	{
		pBeam = CBeam::BeamCreate( GAUSS_BEAM_SPRITE, (width/2.0f) + i );
		
		if ( useMuzzle )
		{
			pBeam->PointEntInit( endPos, m_hViewModel );
			pBeam->SetEndAttachment( 1 );
		}
		else
		{
			pBeam->SetStartPos( startPos );
			pBeam->SetEndPos( endPos );
		}
		
		pBeam->SetBrightness( random->RandomInt( 64, 255 ) );
		pBeam->SetColor( 255, 255, 150+random->RandomInt( 0, 64 ) );
		pBeam->RelinkBeam();
		pBeam->LiveForTime( 0.1f );
		pBeam->SetNoise( 1.6f * i );
		pBeam->SetEndWidth( 0.1f );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponTauCannon::PrimaryAttack( void )
{
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	
	if ( !pOwner ){ return; }

	WeaponSound( SINGLE );
	WeaponSound( SPECIAL2 );
	
	SendWeaponAnim( ACT_VM_PRIMARYATTACK );
	
	pOwner->DoMuzzleFlash();

	m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();

	pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );

	Fire();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponTauCannon::IncreaseCharge( void )
{
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );

	if ( m_flNextChargeTime > gpGlobals->curtime || !pOwner ){ return; }

	// Сверим время накопления
	if ( ( gpGlobals->curtime - m_flChargeStartTime ) > MAX_GAUSS_CHARGE_TIME )
	{
		// Предупредим игрока что он на максимальной стадии накопления
		if ( m_bChargeIndicated == false )
		{
			WeaponSound( SPECIAL2 );
			m_bChargeIndicated = true;
		}

		if ( ( gpGlobals->curtime - m_flChargeStartTime ) > DANGER_GAUSS_CHARGE_TIME )
		{
			// Раним игрока за то, что он передержал
			WeaponSound( SPECIAL2 );
			
			// Добавим DMG_CRUSH потому что не хотим никакой физической силы
			pOwner->TakeDamage( CTakeDamageInfo( this, this, 25, DMG_SHOCK | DMG_CRUSH ) );
			
			color32 gaussDamage = {255,128,0,128};
			UTIL_ScreenFade( pOwner, gaussDamage, 0.2f, 0.2f, FFADE_IN ); // подсветим жкран

			m_flNextChargeTime = gpGlobals->curtime + random->RandomFloat( 0.5f, 2.5f );
		}

		return;
	}

	// Вычитаем силу
	pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
	// Контролируем подачу тона звука
	int pitch = ( gpGlobals->curtime - m_flChargeStartTime ) * ( 150 / GetFullChargeTime() ) + 100;
	if ( pitch > 250 ){ pitch = 250; }
	if ( m_sndCharge != NULL )
	{
		(CSoundEnvelopeController::GetController()).SoundChangePitch( m_sndCharge, pitch, 0 );
	}

	// Убедимся что можем высвободить силу
	if ( pOwner->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
	{
		ChargedFire();
		return;
	}

	m_flNextChargeTime = gpGlobals->curtime + GAUSS_CHARGE_TIME;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CWeaponTauCannon::SecondaryAttack( void )
{
	CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
	
	if ( !pOwner || pOwner->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 ){ return; }
	// Не стреляем под водой
	if ( pOwner->GetWaterLevel() == 3 )
	{
		EmitSound( "Weapon_Gauss.Zap1" );
		SendWeaponAnim( ACT_VM_IDLE );
		m_flNextSecondaryAttack = m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
		return;
	}
	if ( !m_bCharging )
	{
		// Начинаем раскруточную анимацию
		SendWeaponAnim( ACT_VM_PULLBACK );
		
		// Начинаем повторяющийся звук
		if ( !m_sndCharge )
		{
			CPASAttenuationFilter filter( this );
			m_sndCharge	= (CSoundEnvelopeController::GetController()).SoundCreate( filter, entindex(), CHAN_STATIC, "weapons/gauss/chargeloop.wav", ATTN_NORM );
		}

		if ( m_sndCharge != NULL )
		{
			(CSoundEnvelopeController::GetController()).Play( m_sndCharge, 1.0f, 50 );
			(CSoundEnvelopeController::GetController()).SoundChangePitch( m_sndCharge, 250, 3.0f );
		}

		m_flChargeStartTime = gpGlobals->curtime;
		m_bCharging = true;
		m_bChargeIndicated = false;

		// Вычитаем патроны
		pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
	}

	IncreaseCharge();
}

//-----------------------------------------------------------------------------
// Purpose:  Дефолтная качка экрана
//-----------------------------------------------------------------------------
void CWeaponTauCannon::AddViewKick( void )
{
	//Get the view kick
	CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );

	if ( pPlayer == NULL )
		return;

	QAngle	viewPunch;

	viewPunch.x = random->RandomFloat( -0.5f, -0.2f );
	viewPunch.y = random->RandomFloat( -0.5f,  0.5f );
	viewPunch.z = 0;

	pPlayer->ViewPunch( viewPunch );
}

//-----------------------------------------------------------------------------
// Purpose: Покадровый чекер
//-----------------------------------------------------------------------------
void CWeaponTauCannon::ItemPostFrame( void )
{
	CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );

	if ( !pPlayer ){ return; }

	if ( pPlayer->m_afButtonReleased & IN_ATTACK2 )
	{
		if ( m_bCharging ){ ChargedFire(); }
	}
	
	BaseClass::ItemPostFrame();
}

//-----------------------------------------------------------------------------
// Purpose: Останавливаем раскруточный звук
//-----------------------------------------------------------------------------
void CWeaponTauCannon::StopChargeSound( void )
{
	if ( m_sndCharge != NULL )
	{
		(CSoundEnvelopeController::GetController()).SoundFadeOut( m_sndCharge, 0.1f );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Сворачивание оружия в кобуру
// Input  : *pSwitchingTo - 
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CWeaponTauCannon::Holster( CBaseCombatWeapon *pSwitchingTo )
{
	StopChargeSound(); // останавливаем звук раскрутки
	m_bCharging = false;
	m_bChargeIndicated = false;

	return BaseClass::Holster( pSwitchingTo );
}
//-----------------------------------------------
// Purpose: Время на раскрутку
//-----------------------------------------------
float CWeaponTauCannon::GetFullChargeTime( void )
{
	if ( g_pGameRules->IsMultiplayer() )
	{
		return 1.5;
	}
	else
	{
		return 4;
	}
}
//----------------------------------------------------------------------------------
// Purpose: Добавляет брызги на воду при стрельбе по оной 
//----------------------------------------------------------------------------------
#define FSetBit(iBitVector, bits)	((iBitVector) |= (bits)) // LOLMEN : Set some bits to bit vec
#define FBitSet(iBitVector, bit)	((iBitVector) & (bit))	// LOLMEN : Do that bit setted in bit vec?
#define TraceContents( vec ) ( enginetrace->GetPointContents( vec ) ) // LOLMEN : Do some test?
#define WaterContents( vec ) ( FBitSet( TraceContents( vec ), CONTENTS_WATER|CONTENTS_SLIME ) ) // Lolmen : For water

bool CWeaponTauCannon::ShouldDrawWaterImpacts( const trace_t &shot_trace )
{
	//FIXME: This doesn't handle the case of trying to splash while being underwater, but that's not going to look good
	//		 right now anyway...

	// We must start outside the water
	if ( WaterContents( shot_trace.startpos ) )
		return false;

	// We must end inside of water
	if ( !WaterContents( shot_trace.endpos ) )
		return false;

	trace_t	waterTrace;

	UTIL_TraceLine( shot_trace.startpos, shot_trace.endpos, (CONTENTS_WATER|CONTENTS_SLIME), UTIL_GetLocalPlayer(), COLLISION_GROUP_NONE, &waterTrace );


	if ( waterTrace.fraction < 1.0f )
	{
		CEffectData	data;

		data.m_fFlags  = 0;
		data.m_vOrigin = waterTrace.endpos;
		data.m_vNormal = waterTrace.plane.normal;
		data.m_flScale = random->RandomFloat(2.0,4.0f); // Lolmen : Регулируйте размер кругов/брызг тут

		// See if we hit slime
		if ( FBitSet( waterTrace.contents, CONTENTS_SLIME ) )
		{
			FSetBit( data.m_fFlags, FX_WATER_IN_SLIME );
		}
		
		CPASFilter filter( data.m_vOrigin ); 
		te->DispatchEffect( filter, 0.0, data.m_vOrigin, "watersplash", data );	
	}
	return true;
}

C серверной частью закончили, теперь займёмся клиентской...

зайдем в moddir\src\cl_dll\hl2_hud\ и откроем там c_weapon__stubs_hl2.cpp

В нём вы увидите список оружия, так сразу закомментируем строчку вот так:

//STUB_WEAPON_CLASS( weapon_gauss, WeaponGaussGun, C_BaseHLCombatWeapon );
STUB_WEAPON_CLASS( weapon_cubemap, WeaponCubemap, C_BaseCombatWeapon );

И куданить например после:

STUB_WEAPON_CLASS( weapon_357, Weapon357, C_BaseHLCombatWeapon );

Вставим строчечку:

STUB_WEAPON_CLASS( weapon_gauss, WeaponTauCannon, C_BaseHLCombatWeapon );

Так, теперь есть файл который и на клиенсткой и на серверной части, hl2_gamerules.cpp в нём найдём

CAmmoDef *GetAmmoDef()

После этой строчки будет идти список типов патронов доступных в игре... Впишем туда после

def.AddAmmoType("GaussEnergy",	DMG_SHOCK, TRACER_NONE,	"sk_jeep_gauss_damage",	"sk_jeep_gauss_damage", "sk_max_gauss_round", BULLET_IMPULSE(650, 8000), 0 ); 

вот это:

def.AddAmmoType("Uranium", DMG_SHOCK | DMG_ENERGYBEAM, TRACER_NONE, "sk_plr_dmg_gauss", "sk_npc_dmg_gauss", "sk_max_gauss_round", BULLET_IMPULSE(650, 8000), 0 ); 

Это необходимо, чтобы багги не конфликтовал с ручным гауссом.

также там-же найдём :

ConVar	sk_max_gauss_round		( "sk_max_gauss_round", "300", FCVAR_REPLICATED );

И перед, впишем:

ConVar sk_plr_dmg_gauss( "sk_plr_dmg_gauss", "0" );
ConVar sk_npc_dmg_gauss( "sk_plr_dmg_gauss", "0" );
ConVar sk_plr_max_dmg_gauss( "sk_plr_max_dmg_gauss", "0" );

Эти переменные будем контролировать из skill.cfg который должен храниться в moddir/cfg

Дальше, нам хотелось бы заюзать коробочки с патронами для гаусса... Откроем moddir\src\dlls\hl2_dll\item_ammo.cpp

И разместим там где-нить после строчки :

LINK_ENTITY_TO_CLASS( item_rpg_round, CItem_RPG_Round );

Такое:

#define AMMO_GAUSS_GIVE		20
#define AMMO_GAUSS_MODEL	"models/w_gaussammo.mdl" // УКАЖИТЕ вашу модель для коробочки тут

class CItem_GaussAmmo : public CItem
{
public:
	DECLARE_CLASS( CItem_GaussAmmo, CItem );

	void Spawn( void )
	{ 
		Precache();
		SetModel( AMMO_GAUSS_MODEL );
		BaseClass::Spawn();
	}
	void Precache( void )
	{
		engine->PrecacheModel( AMMO_GAUSS_MODEL );
	}
	bool MyTouch( CBasePlayer *pPlayer )
	{
	        if ( ITEM_GiveAmmo( pPlayer, AMMO_GAUSS_GIVE, "Uranium" ) )
		{
			if ( g_pGameRules->ItemShouldRespawn( this ) == GR_ITEM_RESPAWN_NO )
			{
				UTIL_Remove(this);	
			}	
			return true;
		}
		return false;
	}
};
LINK_ENTITY_TO_CLASS( item_ammo_gauss, CItem_GaussAmmo ); // Название инить
LINK_ENTITY_TO_CLASS( item_gauss_ammo, CItem_GaussAmmo ); // Название инить

Агась :D А игрока проэквипить забыли? Ну помните impulse 101? :D Так, тогда лезем в player.cpp и ищем там: case 101: и там уже по вашему усмотрению... А по моему добавим там:

GiveAmmo( 150,  "Uranium" );
GiveNamedItem( "weapon_gauss" );

Так теперь идём в действительную папку с модом, залазим там в scripts, и создаём текстовик weapon_gauss.txt вот с такими внутренностями :

// Gauss Gun

WeaponData
{
	// Weapon fdata is loaded by both the Game and Client DLLs.
	"printname"			"Tau Cannon"     // Сделайте имя в resources если надо и юзайте #ResourcesName
	"viewmodel"			"models/weapons/v_pistol.mdl" // LOLMEN : поставьте вашу модель
	"playermodel"			"models/weapons/w_pistol.mdl" // LOLMEN : поставьте вашу модель
	"anim_prefix"			"gauss"
	"bucket"			"4" // --- \|/--------------
	"bucket_position"		"5" // Отрегулируйте положиение в слоте

	"clip_size"			"-1"
	"default_clip"			"20"

	"primary_ammo"			"Uranium"
	"secondary_ammo"		"None"

	"weight"			"2"
	"item_flags"			"0"

	// Sounds for the weapon. There is a max of 16 sounds per category (i.e. max 16 "single_shot" sounds)
	SoundData
	{
		"single_shot"			"Weapon_Gauss.Single"
		"special1"			"Weapon_Gauss.Special1"
		"special2"			"Weapon_Gauss.Special2"	
	}

	// Weapon Sprite data is loaded by the Client DLL.
	TextureData
	{
		"weapon"
		{
				"file"		"sprites/w_icons1"
				"x"			"128"
				"y"			"64"
				"width"		"128"
				"height"	"64"
		}
		"weapon_s"
		{
				"file"		"sprites/w_icons1b"
				"x"			"128"
				"y"			"64"
				"width"		"128"
				"height"	"64"
		}
		"ammo"
		{
				"file"		"sprites/a_icons1"
				"x"			"55"
				"y"			"130"
				"width"		"73"
				"height"	"20"
		}
		"crosshair"
		{
				"file"		"sprites/crosshairs"
				"x"			"0"
				"y"			"48"
				"width"		"24"
				"height"	"24"
		}
		"autoaim"
		{
				"file"		"sprites/crosshairs"
				"x"			"0"
				"y"			"48"
				"width"		"24"
				"height"	"24"
		}
	}
}

Еще придётся где-то разжиться и положить три electro звука, вот по таким путям:

moddir\sound\weapons\gauss\electro1.wav
moddir\sound\weapons\gauss\electro2.wav
moddir\sound\weapons\gauss\electro3.wav

Если возникнут проблемы, обращаемся в форум.

Автор: Lolmen