Сегодня мы займёмся созданием гауссовой винтовки :) Иными словами Гауссовки :) для оружия применим более рациональное название 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
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_gauss.mdl"
"playermodel" "models/weapons/w_gauss.mdl"
"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
Если возникнут проблемы, обращаемся на форум