Paranoia2/dlls/weapons.cpp

3069 lines
82 KiB
C++

/***
*
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
/*
===== weapons.cpp ========================================================
functions governing the selection/use of weapons for players
*/
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "player.h"
#include "monsters.h"
#include "weapons.h"
#include "nodes.h"
#include "soundent.h"
#include "decals.h"
#include "gamerules.h"
#include "animation.h"
#include "game.h"
extern int gEvilImpulse101;
DLL_GLOBAL unsigned short g_usShootEvent;
DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam
DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr";
DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot
DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball
DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud
DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion
DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model
DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood
DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood
DLL_GLOBAL short g_sModelIndexWaterSplash;
DLL_GLOBAL short g_sModelIndexSmokeTrail;
DLL_GLOBAL short g_sModelIndexNull;
ItemInfo CBasePlayerItem :: ItemInfoArray[MAX_WEAPONS];
int CBasePlayerItem :: m_iGlobalID;
extern int gmsgCurWeapon;
// Precaches the weapon and queues the weapon info for sending to clients
void UTIL_PrecacheWeapon( const char *szClassname )
{
edict_t *pent = CREATE_NAMED_ENTITY( ALLOC_STRING( szClassname ) );
if ( FNullEnt( pent ))
{
ALERT( at_error, "NULL Ent in UTIL_PrecacheOtherWeapon\n" );
return;
}
CBaseEntity *pEntity = CBaseEntity::Instance (VARS( pent ));
if (pEntity)
{
ItemInfo II;
memset( &II, 0, sizeof II );
if((( CBasePlayerItem *)pEntity)->GetItemInfo( &II ))
{
CBasePlayerItem::ItemInfoArray[II.iId] = II;
}
// g-cont. reduce loading time because v_models is too heaviliy ~ 35 Mb
// pEntity->Precache( );
}
REMOVE_ENTITY( pent );
}
void UTIL_PrecacheSpecialItems( void ) // buz
{
ItemInfo II;
// add painkiller to weapons list
memset( &II, 0, sizeof II );
II.pszName = "painkiller";
II.pszAmmo1 = "painkillers";
II.iMaxAmmo1 = 10;
II.iSlot = 0;
II.iPosition = 1;
II.pszAmmo2 = "none";
II.iMaxAmmo2 = -1;
II.iId = WEAPON_PAINKILLER;
CBasePlayerItem::ItemInfoArray[II.iId] = II;
}
void UTIL_InitWeaponDescription( const char *pattern )
{
int numFiles = 0;
char **filenames = GET_FILES_LIST( pattern, &numFiles, FALSE );
char classname[256]; // convert path into name
for( int i = 0; i < numFiles; i++ )
{
COM_FileBase( filenames[i], classname );
UTIL_PrecacheWeapon( classname );
}
UTIL_PrecacheSpecialItems();
}
TYPEDESCRIPTION CBasePlayerItem :: m_SaveData[] =
{
DEFINE_FIELD( CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR ),
DEFINE_FIELD( CBasePlayerItem, m_pNext, FIELD_CLASSPTR ),
DEFINE_FIELD( CBasePlayerItem, m_iId, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iItemCaps, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iDefaultAmmo1, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iDefaultAmmo2, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_flNextPrimaryAttack, FIELD_TIME ),
DEFINE_FIELD( CBasePlayerItem, m_flNextSecondaryAttack, FIELD_TIME ),
DEFINE_FIELD( CBasePlayerItem, m_flTimeWeaponIdle, FIELD_TIME ),
DEFINE_FIELD( CBasePlayerItem, m_flTimeUpdate, FIELD_TIME ),
DEFINE_FIELD( CBasePlayerItem, m_flSpreadTime, FIELD_FLOAT ),
DEFINE_FIELD( CBasePlayerItem, m_flLastShotTime, FIELD_TIME ),
DEFINE_FIELD( CBasePlayerItem, m_flLastSpreadPower, FIELD_FLOAT ),
DEFINE_FIELD( CBasePlayerItem, m_iPrimaryAmmoType, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iSecondaryAmmoType, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iWeaponAutoFire, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_cActiveRockets, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iStepReload, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iIronSight, FIELD_BOOLEAN ),
DEFINE_FIELD( CBasePlayerItem, m_fWaitForHolster, FIELD_BOOLEAN ),
DEFINE_FIELD( CBasePlayerItem, m_iZoom, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iClip, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iBody, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iSkin, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iSpot, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerItem, m_iHandModel, FIELD_MODELNAME ),
}; IMPLEMENT_SAVERESTORE( CBasePlayerItem, CBaseAnimating );
LINK_ENTITY_TO_CLASS( weapon_generic, CBasePlayerItem );
void CBasePlayerItem :: SetObjectCollisionBox( void )
{
pev->absmin = pev->origin + Vector( -24.0f, -24.0f, 0.0f );
pev->absmax = pev->origin + Vector( 24.0f, 24.0f, 16.0f );
}
//=========================================================
// Sets up movetype, size, solidtype for a new weapon.
//=========================================================
void CBasePlayerItem :: FallInit( void )
{
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_BBOX;
UTIL_SetOrigin( this, pev->origin );
UTIL_SetSize( pev, g_vecZero, g_vecZero ); // pointsize until it lands on the ground.
// Wargon: Оружие юзабельно.
SetUse( &CBasePlayerItem::DefaultUse );
m_iItemCaps = CBaseEntity::ObjectCaps() | FCAP_IMPULSE_USE;
if( !FBitSet( ObjectCaps(), FCAP_USE_ONLY ) || FBitSet( pev->spawnflags, SF_NORESPAWN ))
SetTouch( &CBasePlayerItem :: DefaultTouch );
SetThink( &CBasePlayerItem :: FallThink );
SetNextThink( 0.1 );
}
//=========================================================
// AttemptToMaterialize - the item is trying to rematerialize,
// should it do so now or wait longer?
//=========================================================
void CBasePlayerItem :: AttemptToMaterialize( void )
{
float time = g_pGameRules->FlWeaponTryRespawn( this );
if ( time == 0.0f )
{
Materialize();
return;
}
SetNextThink( time );
}
//=========================================================
// Materialize - make a CBasePlayerItem visible and tangible
//=========================================================
void CBasePlayerItem :: Materialize( void )
{
if ( pev->effects & EF_NODRAW )
{
// changing from invisible state to visible.
EMIT_SOUND_DYN( edict(), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 );
pev->effects &= ~EF_NODRAW;
pev->effects |= EF_MUZZLEFLASH;
}
pev->solid = SOLID_TRIGGER;
UTIL_SetOrigin( this, pev->origin ); // link into world.
// Wargon: Оружие юзабельно.
SetUse( &CBasePlayerItem::DefaultUse );
m_iItemCaps = CBaseEntity::ObjectCaps() | FCAP_IMPULSE_USE;
if( !FBitSet( ObjectCaps(), FCAP_USE_ONLY ))
SetTouch( &CBasePlayerItem :: DefaultTouch );
SetThink( NULL );
}
//=========================================================
// CheckRespawn - a player is taking this weapon, should
// it respawn?
//=========================================================
void CBasePlayerItem :: CheckRespawn ( void )
{
switch ( g_pGameRules->WeaponShouldRespawn( this ) )
{
case GR_WEAPON_RESPAWN_YES:
Respawn();
break;
case GR_WEAPON_RESPAWN_NO:
break;
}
}
//=========================================================
// Respawn- this item is already in the world, but it is
// invisible and intangible. Make it visible and tangible.
//=========================================================
CBaseEntity* CBasePlayerItem :: Respawn( void )
{
// make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
// will decide when to make the weapon visible and touchable.
const char *pszClassname = STRING( pev->classname );
CBaseEntity *pNewWeapon = CBaseEntity :: Create( pszClassname, g_pGameRules->VecWeaponRespawnSpot( this ), pev->angles, pev->owner );
if ( pNewWeapon )
{
pNewWeapon->pev->effects |= EF_NODRAW;// invisible for now
// Wargon: Оружие неюзабельно.
pNewWeapon->SetUse( NULL );
m_iItemCaps = CBaseEntity :: ObjectCaps();
pNewWeapon->SetTouch( NULL );// no touch
pNewWeapon->SetThink( &CBasePlayerItem :: AttemptToMaterialize );
DROP_TO_FLOOR( edict() );
// not a typo! We want to know when the weapon the player just picked up should respawn!
// this new entity we created is the replacement,
// but when it should respawn is based on conditions belonging to the weapon that was taken.
pNewWeapon->AbsoluteNextThink( g_pGameRules->FlWeaponRespawnTime( this ));
}
else
{
ALERT ( at_debug, "Respawn failed to create %s!\n", STRING( pev->classname ) );
}
return pNewWeapon;
}
//=========================================================
// FallThink - Items that have just spawned run this think
// to catch them when they hit the ground. Once we're sure
// that the object is grounded, we change its solid type
// to trigger and set it in a large box that helps the
// player get it.
//=========================================================
void CBasePlayerItem :: FallThink ( void )
{
SetNextThink( 0.1 );
if ( pev->flags & FL_ONGROUND )
{
// clatter if we have an owner (i.e., dropped by someone)
// don't clatter if the gun is waiting to respawn (if it's waiting, it is invisible!)
if ( !FNullEnt( pev->owner ) )
{
int pitch = 95 + RANDOM_LONG( 0, 29 );
EMIT_SOUND_DYN( edict(), CHAN_VOICE, "items/weapondrop1.wav", 1, ATTN_NORM, 0, pitch );
}
// lie flat
pev->angles.x = 0;
pev->angles.z = 0;
Materialize();
}
}
void CBasePlayerItem :: KnifeDecal1( void )
{
UTIL_StudioDecalTrace( &m_trHit, pszDecalName( 0 ));
UTIL_TraceCustomDecal( &m_trHit, pszDecalName( 0 ));
}
void CBasePlayerItem :: KnifeDecal2( void )
{
UTIL_StudioDecalTrace( &m_trHit, pszDecalName( 1 ));
UTIL_TraceCustomDecal( &m_trHit, pszDecalName( 1 ));
}
BOOL CBasePlayerItem :: HasAmmo( void )
{
BOOL bHasAmmo = 0;
if ( pszAmmo1() )
bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] != 0);
if ( pszAmmo2() )
bHasAmmo |= (m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] != 0);
if (m_iClip > 0)
bHasAmmo |= 1;
return bHasAmmo;
}
BOOL CBasePlayerItem :: CanDeploy( void )
{
if( FBitSet( iFlags(), ITEM_FLAG_SELECTONEMPTY ))
return TRUE;
if ( !Q_stricmp( pszAmmo1(), "none" ) && !Q_stricmp( pszAmmo2(), "none" ))
{
// this weapon doesn't use ammo, can always deploy.
return TRUE;
}
return HasAmmo();
}
BOOL CBasePlayerItem :: CanHolster( void )
{
// can't put away while guiding a missile.
if( m_iSpot && m_cActiveRockets )
return FALSE;
return TRUE;
}
void CBasePlayerItem :: Precache( void )
{
int i;
m_iClientAnim = -1;
m_iClientSkin = -1;
m_iClientBody = -1;
if( iViewModel() != iStringNull )
PRECACHE_MODEL( STRING( iViewModel() ));
if( iHandModel() != iStringNull )
PRECACHE_MODEL( STRING( iHandModel() ));
if( iWorldModel() != iStringNull )
PRECACHE_MODEL( STRING( iWorldModel() ));
for( i = 0; i < sndcnt1(); i++ )
PRECACHE_SOUND( STRING( ItemInfoArray[m_iId].shootsound1[i] ));
for( i = 0; i < sndcnt2(); i++ )
PRECACHE_SOUND( STRING( ItemInfoArray[m_iId].shootsound2[i] ));
for( i = 0; i < emptycnt(); i++ )
PRECACHE_SOUND( STRING( ItemInfoArray[m_iId].emptysounds[i] ));
// FIXME: add reload sounds
}
int CBasePlayerItem :: GetItemInfo( ItemInfo *p )
{
// support for half-virtual weapons
if( FStringNull( pev->netname ))
pev->netname = pev->classname;
if( ParseWeaponFile( p, STRING( pev->netname )))
{
GenerateID();
ALERT( at_aiconsole, "ID %i for %s\n", m_iId, STRING( pev->netname ));
p->iId = m_iId;
return 1;
}
return 0;
}
void CBasePlayerItem :: Spawn( void )
{
// support for half-virtual weapons
if( FStringNull( pev->netname ))
pev->netname = pev->classname;
if( !FindWeaponID( )) // get actual ID
{
ALERT( at_error, "No spawn function for %s\n", STRING( pev->netname ));
UTIL_Remove( this );
return;
}
Precache();
// init default ammo
m_iDefaultAmmo1 = iDefaultAmmo1();
m_iDefaultAmmo2 = iDefaultAmmo2();
m_iHandModel = iHandModel();
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_BBOX;
pev->framerate = 1.0f;
UTIL_SetOrigin( this, pev->origin );
UTIL_SetSize( pev, g_vecZero, g_vecZero ); // pointsize until it lands on the ground.
if( !FBitSet( ObjectCaps(), FCAP_USE_ONLY ) || FBitSet( pev->spawnflags, SF_NORESPAWN ))
SetTouch( DefaultTouch );
SetThink( FallThink );
SET_MODEL( edict(), STRING( iWorldModel( )));
if( GetSequenceCount( ) > 1 )
pev->sequence = 1; // set world animation
pev->animtime = gpGlobals->time + 0.1;
m_iSpot = 0;
SetNextThink( 0.1 );
}
bool CBasePlayerItem :: FindWeaponID( void )
{
for( int i = 0; i < m_iGlobalID; i++ )
{
if( FStrEq( STRING( pev->netname ), ItemInfoArray[i].pszName ))
{
// already exist
m_iId = ItemInfoArray[i].iId;
return true;
}
}
return false;
}
void CBasePlayerItem :: GenerateID( void )
{
if( FindWeaponID() )
return; // already exist
if( m_iGlobalID >= WEAPON_CUSTOM_COUNT )
{
ALERT( at_error, "GenerateID: unique weapon ID's is out. Limit is %i weapons\n", WEAPON_CUSTOM_COUNT );
m_iId = 0;
return;
}
m_iId = m_iGlobalID++;
}
void CBasePlayerItem :: DefaultTouch( CBaseEntity *pOther )
{
// if it's not a player, ignore
if ( !pOther->IsPlayer( ))
return;
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
// can I have this?
if ( !g_pGameRules->CanHavePlayerItem( pPlayer, this ) )
{
if ( gEvilImpulse101 )
{
UTIL_Remove( this );
}
return;
}
if ( pOther->AddPlayerItem( this ))
{
EMIT_SOUND( edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM );
AttachToPlayer( pPlayer );
}
SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // UNDONE: when should this happen?
}
void CBasePlayerItem :: SetDefaultParams( ItemInfo *II )
{
II->iSlot = II->iPosition = 0;
II->iViewModel = MAKE_STRING( "models/view_glock.mdl" );
II->iHandModel = iStringNull;
II->iWorldModel = MAKE_STRING( "models/world_glock.mdl" );
Q_strcpy( II->szAnimExt, "onehanded" );
II->pszAmmo1 = II->pszAmmo2 = "none";
II->iMaxAmmo1 = II->iMaxAmmo2 = -1;
II->iMaxClip = -1;
II->iFlags = II->iWeight = 0;
II->attack1 = ATTACK_NONE;
II->attack2 = ATTACK_NONE;
II->fNextAttack1 = II->fNextAttack2 = 0.5f;
memset( II->shootsound1, 0, sizeof( II->shootsound1 ));
memset( II->shootsound2, 0, sizeof( II->shootsound2 ));
memset( II->emptysounds, 0, sizeof( II->emptysounds ));
II->sndcount1 = II->sndcount2 = II->emptysndcount = 0;
II->feedback1[0].punchangle[0] = II->feedback1[0].punchangle[2] = II->feedback1[0].punchangle[2] = RandomRange( 0.0f );
II->feedback1[1].punchangle[0] = II->feedback1[1].punchangle[2] = II->feedback1[1].punchangle[2] = RandomRange( 0.0f );
II->feedback2[0].punchangle[0] = II->feedback2[0].punchangle[2] = II->feedback2[0].punchangle[2] = RandomRange( 0.0f );
II->feedback2[1].punchangle[0] = II->feedback2[1].punchangle[2] = II->feedback2[1].punchangle[2] = RandomRange( 0.0f );
II->feedback1[0].recoil = II->feedback1[1].recoil = RandomRange( 0.0f );
II->feedback2[0].recoil = II->feedback2[1].recoil = RandomRange( 0.0f );
II->plr_settings[0].jumpHeight = -1.0f;
II->plr_settings[1].jumpHeight = -1.0f;
II->plr_settings[0].maxSpeed = 0;
II->plr_settings[1].maxSpeed = 0;
II->iVolume = NORMAL_GUN_VOLUME;
II->iFlash = NORMAL_GUN_FLASH;
II->recoil1 = RandomRange( 0.0f );
II->recoil2 = RandomRange( 0.0f );
II->vecThrowOffset = g_vecZero;
II->spread1[0].range = RandomRange( 6.0f, 6.0f );
II->spread1[0].type = SPREAD_LINEAR;
II->spread1[0].expand = 0.25f;
II->spread1[1].range = RandomRange( 2.0f, 4.0f );
II->spread1[1].type = SPREAD_LINEAR;
II->spread1[1].expand = 0.2f;
II->spread2[0].range = RandomRange( 6.0f, 6.0f );
II->spread2[0].type = SPREAD_LINEAR;
II->spread2[0].expand = 0.25f;
II->spread2[1].range = RandomRange( 2.0f, 4.0f );
II->spread2[1].type = SPREAD_LINEAR;
II->spread2[1].expand = 0.2f;
II->spreadtime = 1.5f;
m_iDefaultAmmo1 = 0;
m_iDefaultAmmo2 = 0;
m_iHandModel = 0;
m_iId = 0; // will be overwritten with GenerateID()
}
int CBasePlayerItem :: ParseWeaponFile( ItemInfo *II, const char *filename )
{
char path[256];
int iResult = 0;
Q_snprintf( path, sizeof( path ), "scripts/weapons/%s", filename );
COM_DefaultExtension( path, ".txt" );
char *afile = (char *)LOAD_FILE( path, NULL );
SetDefaultParams( II );
if( !afile )
{
ALERT( at_warning, "weapon info file for %s not found! Entity removed from map.\n", STRING( pev->netname ));
UTIL_Remove( this );
return 0;
}
else
{
II->pszName = STRING( pev->netname );
ALERT( at_aiconsole, "parse %s.txt\n", II->pszName );
// parses the type, moves the file pointer
iResult = ParseWeaponData( II, afile );
ALERT( at_aiconsole, "Parsing: WeaponData{} %s\n", iResult ? "OK" : "ERROR" );
iResult = ParsePrimaryAttack( II, afile );
ALERT( at_aiconsole, "Parsing: PrimaryAttack{} %s\n", iResult ? "OK" : "ERROR" );
iResult = ParseSecondaryAttack( II, afile );
ALERT( at_aiconsole, "Parsing: SecondaryAttack{} %s\n", iResult ? "OK" : "ERROR" );
iResult = ParseSoundData( II, afile );
ALERT( at_aiconsole, "Parsing: SoundData{} %s\n", iResult ? "OK" : "ERROR" );
FREE_FILE( afile );
return 1;
}
}
int CBasePlayerItem :: ParseItemFlags( char *pfile )
{
char token[256];
int iFlags = 0;
if( !pfile || !*pfile )
return iFlags;
while( pfile != NULL )
{
pfile = COM_ParseLine( pfile, token );
if( !Q_stricmp( token, "SelectOnEmpty" ))
iFlags |= ITEM_FLAG_SELECTONEMPTY;
else if( !Q_stricmp( token, "NoAutoReload" ))
iFlags |= ITEM_FLAG_NOAUTORELOAD;
else if( !Q_stricmp( token, "NoAutoSwitch" ))
iFlags |= ITEM_FLAG_NOAUTOSWITCHEMPTY;
else if( !Q_stricmp( token, "LimitInWorld" ))
iFlags |= ITEM_FLAG_LIMITINWORLD;
else if( !Q_stricmp( token, "Exhaustible" ))
iFlags |= ITEM_FLAG_EXHAUSTIBLE;
else if( !Q_stricmp( token, "NoDuplicate" ))
iFlags |= ITEM_FLAG_NODUPLICATE;
else if( !Q_stricmp( token, "AutoAim" ))
iFlags |= ITEM_FLAG_USEAUTOAIM;
else if( !Q_stricmp( token, "AllowFireMode" ))
iFlags |= ITEM_FLAG_ALLOW_FIREMODE;
else if( !Q_stricmp( token, "UnderWater" ))
iFlags |= ITEM_FLAG_SHOOT_UNDERWATER;
else if( !Q_stricmp( token, "IronSight" ))
iFlags |= ITEM_FLAG_IRONSIGHT;
else if( !Q_stricmp( token, "AutoFire" ))
iFlags |= ITEM_FLAG_AUTOFIRE;
else if( !Q_stricmp( token, "Scope" ))
iFlags |= ITEM_FLAG_SCOPE;
else if( !Q_stricmp( token, "NoDrop" ))
iFlags |= ITEM_FLAG_NODROP;
else if( pfile && token[0] != '|' )
ALERT( at_warning, "unknown value %s for 'item_flags'\n", token );
}
return iFlags;
}
char *CBasePlayerItem :: ParseViewPunch( char *pfile, feedback_t *pFeed )
{
char token[256];
for( int i = 0; i < 3 && pfile != NULL; i++ )
{
pfile = COM_ParseLine( pfile, token );
pFeed->punchangle[i] = RandomRange( token );
}
return pfile;
}
int CBasePlayerItem :: ParseWeaponData( ItemInfo *II, char *pfile )
{
char token[2048];
while( Q_stricmp( token, "WeaponData" ))
{
if( !pfile ) return 0;
pfile = COM_ParseFile( pfile, token );
}
while( Q_stricmp( token, "{" ))
{
if( !pfile ) return 0;
pfile = COM_ParseFile( pfile, token );
}
pfile = COM_ParseFile( pfile, token );
while( Q_stricmp( token, "}" ))
{
if( !pfile ) return 0;
if ( !Q_stricmp( token, "viewmodel" ))
{
pfile = COM_ParseFile( pfile, token );
II->iViewModel = ALLOC_STRING( token );
}
else if ( !Q_stricmp( token, "handmodel" ))
{
pfile = COM_ParseFile( pfile, token );
II->iHandModel = ALLOC_STRING( token );
}
else if( !Q_stricmp( token, "playermodel" ))
{
pfile = COM_ParseFile( pfile, token );
II->iWorldModel = ALLOC_STRING( token );
}
else if( !Q_stricmp( token, "anim_prefix" ))
{
pfile = COM_ParseFile( pfile, token );
Q_strncpy( II->szAnimExt, token, sizeof( II->szAnimExt ));
}
else if( !Q_stricmp( token, "bucket" ))
{
pfile = COM_ParseFile( pfile, token );
II->iSlot = Q_atoi( token );
}
else if( !Q_stricmp( token, "bucket_position" ))
{
pfile = COM_ParseFile( pfile, token );
II->iPosition = Q_atoi( token );
}
else if( !Q_stricmp( token, "clip_size" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( token, "noclip" ))
II->iMaxClip = WEAPON_NOCLIP;
else II->iMaxClip = Q_atoi( token );
}
else if( !Q_stricmp( token, "primary_ammo" ))
{
pfile = COM_ParseFile( pfile, token );
if( Q_stricmp( token, "none" ))
{
AmmoInfo *pAmmo = UTIL_FindAmmoType( token );
if( pAmmo ) II->iMaxAmmo1 = pAmmo->iMaxCarry;
else ALERT( at_error, "ParseWeaponData: unknown ammo type %s in 'primary_ammo'\n", token );
}
else II->iMaxAmmo1 = -1;
II->pszAmmo1 = STRING( ALLOC_STRING( token ));
}
else if( !Q_stricmp( token, "secondary_ammo" ))
{
pfile = COM_ParseFile( pfile, token );
if( Q_stricmp( token, "none" ))
{
AmmoInfo *pAmmo = UTIL_FindAmmoType( token );
if( pAmmo ) II->iMaxAmmo2 = pAmmo->iMaxCarry;
else ALERT( at_error, "ParseWeaponData: unknown ammo type %s in 'secondary_ammo'\n", token );
}
else II->iMaxAmmo2 = -1;
II->pszAmmo2 = STRING( ALLOC_STRING( token ));
}
else if( !Q_stricmp( token, "defaultammo" ) || !Q_stricmp( token, "defaultammo1" ))
{
pfile = COM_ParseFile( pfile, token );
II->iDefaultAmmo1 = RandomRange( token );
}
else if( !Q_stricmp( token, "defaultammo2" ))
{
pfile = COM_ParseFile( pfile, token );
II->iDefaultAmmo2 = RandomRange( token );
}
else if( !Q_stricmp( token, "weight" ))
{
pfile = COM_ParseFile( pfile, token );
II->iWeight = Q_atoi( token );
}
else if( !Q_stricmp( token, "ThrowOffset" ))
{
pfile = COM_ParseFile( pfile, token );
UTIL_StringToVector( II->vecThrowOffset, token );
}
else if( !Q_stricmp( token, "volume" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( token, "none" ))
II->iVolume = NO_GUN_VOLUME;
else if( !Q_stricmp( token, "quiet" ))
II->iVolume = QUIET_GUN_VOLUME;
else if( !Q_stricmp( token, "normal" ))
II->iVolume = NORMAL_GUN_VOLUME;
else if( !Q_stricmp( token, "loud" ))
II->iVolume = LOUD_GUN_VOLUME;
}
else if( !Q_stricmp( token, "flash" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( token, "none" ))
II->iVolume = NO_GUN_FLASH;
else if( !Q_stricmp( token, "dim" ))
II->iVolume = DIM_GUN_FLASH;
else if( !Q_stricmp( token, "normal" ))
II->iVolume = NORMAL_GUN_FLASH;
else if( !Q_stricmp( token, "bright" ))
II->iVolume = BRIGHT_GUN_FLASH;
}
else if( !Q_stricmp( token, "SpreadTime" ))
{
pfile = COM_ParseFile( pfile, token );
II->spreadtime = Q_atof( token );
}
else if( !Q_stricmp( token, "MaxSpeed" ))
{
pfile = COM_ParseFile( pfile, token );
II->plr_settings[0].maxSpeed = Q_atof( token );
}
else if( !Q_stricmp( token, "JumpHeight" ))
{
pfile = COM_ParseFile( pfile, token );
II->plr_settings[0].jumpHeight = Q_atof( token );
}
else if( !Q_stricmp( token, "MaxSpeedIS" ))
{
pfile = COM_ParseFile( pfile, token );
II->plr_settings[1].maxSpeed = Q_atof( token );
}
else if( !Q_stricmp( token, "JumpHeightIS" ))
{
pfile = COM_ParseFile( pfile, token );
II->plr_settings[1].jumpHeight = Q_atof( token );
}
else if( !Q_stricmp( token, "item_flags" ))
{
pfile = COM_ParseFile( pfile, token );
II->iFlags = ParseItemFlags( token );
}
pfile = COM_ParseFile( pfile, token );
}
return 1;
}
int CBasePlayerItem :: ParsePrimaryAttack( ItemInfo *II, char *pfile )
{
char token[256];
while( Q_stricmp( token, "PrimaryAttack" ))
{
if( !pfile ) return 0;
pfile = COM_ParseFile( pfile, token );
}
while( Q_stricmp( token, "{" ))
{
if( !pfile ) return 0;
pfile = COM_ParseFile( pfile, token );
}
pfile = COM_ParseFile( pfile, token );
while( Q_stricmp( token, "}" ))
{
if( !pfile ) return 0;
if( !Q_stricmp( token, "action" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( token, "none" ))
II->attack1 = ATTACK_NONE;
else if( !Q_stricmp( token, "ammo1" ))
II->attack1 = ATTACK_AMMO1;
else if( !Q_stricmp( token, "ammo2" ))
II->attack1 = ATTACK_AMMO2;
else if( !Q_stricmp( token, "laserdot" ))
II->attack1 = ATTACK_LASER_DOT;
else if( !Q_stricmp( token, "zoom" ))
II->attack1 = ATTACK_ZOOM;
else if( !Q_stricmp( token, "flashlight" ))
II->attack1 = ATTACK_FLASHLIGHT;
else if( !Q_stricmp( token, "switchmode" ))
II->attack1 = ATTACK_SWITCHMODE;
else if( !Q_stricmp( token, "swing" ))
II->attack1 = ATTACK_SWING;
else if( !Q_stricmp( token, "ironsight" ))
II->attack1 = ATTACK_IRONSIGHT;
else if( !Q_stricmp( token, "scope" ))
II->attack1 = ATTACK_SCOPE;
else II->attack1 = ALLOC_STRING( token ); // client command
}
else if( !Q_stricmp( token, "SpreadRange" ))
{
pfile = COM_ParseFile( pfile, token );
II->spread1[0].range = RandomRange( token );
}
else if( !Q_stricmp( token, "SpreadRangeIS" ))
{
pfile = COM_ParseFile( pfile, token );
II->spread1[1].range = RandomRange( token );
}
else if( !Q_stricmp( token, "SpreadType" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( "E_LINEAR", token ))
II->spread1[0].type = SPREAD_LINEAR;
else if( !Q_stricmp( "E_QUAD", token ))
II->spread1[0].type = SPREAD_QUAD;
else if( !Q_stricmp( "E_CUBE", token ))
II->spread1[0].type = SPREAD_CUBE;
else if( !Q_stricmp( "E_SQRT", token ))
II->spread1[0].type = SPREAD_SQRT;
else ALERT( at_warning, "ParsePrimaryAttack: unknown spread equalize type '%s'\n", token );
}
else if( !Q_stricmp( token, "SpreadTypeIS" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( "E_LINEAR", token ))
II->spread1[1].type = SPREAD_LINEAR;
else if( !Q_stricmp( "E_QUAD", token ))
II->spread1[1].type = SPREAD_QUAD;
else if( !Q_stricmp( "E_CUBE", token ))
II->spread1[1].type = SPREAD_CUBE;
else if( !Q_stricmp( "E_SQRT", token ))
II->spread1[1].type = SPREAD_SQRT;
else ALERT( at_warning, "ParsePrimaryAttack: unknown spread equalize type '%s'\n", token );
}
else if( !Q_stricmp( token, "SpreadExpand" ))
{
pfile = COM_ParseFile( pfile, token );
II->spread1[0].expand = Q_atof( token );
}
else if( !Q_stricmp( token, "SpreadExpandIS" ))
{
pfile = COM_ParseFile( pfile, token );
II->spread1[1].expand = Q_atof( token );
}
else if( !Q_stricmp( token, "PunchAngle" ))
{
pfile = ParseViewPunch( pfile, &II->feedback1[0] );
}
else if( !Q_stricmp( token, "PunchAngleIS" ))
{
pfile = ParseViewPunch( pfile, &II->feedback1[1] );
}
else if( !Q_stricmp( token, "recoil" ))
{
pfile = COM_ParseFile( pfile, token );
II->recoil1 = RandomRange( token );
}
else if( !Q_stricmp( token, "nextattack" ))
{
pfile = COM_ParseFile( pfile, token );
II->fNextAttack1 = Q_atof( token );
}
else if( !Q_stricmp( token, "SmashDecal" ))
{
pfile = COM_ParseFile( pfile, token );
II->smashDecals[0] = ALLOC_STRING( token );
}
pfile = COM_ParseFile( pfile, token );
}
return 1;
}
int CBasePlayerItem :: ParseSecondaryAttack( ItemInfo *II, char *pfile )
{
char token[256];
while( Q_stricmp( token, "SecondaryAttack" ))
{
if( !pfile ) return 0;
pfile = COM_ParseFile( pfile, token );
}
while( Q_stricmp( token, "{" ))
{
if( !pfile ) return 0;
pfile = COM_ParseFile( pfile, token );
}
pfile = COM_ParseFile( pfile, token );
while( Q_stricmp( token, "}" ))
{
if( !pfile ) return 0;
if( !Q_stricmp( token, "action" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( token, "none" ))
II->attack2 = ATTACK_NONE;
else if( !Q_stricmp( token, "ammo1" ))
II->attack2 = ATTACK_AMMO1;
else if( !Q_stricmp( token, "ammo2" ))
II->attack2 = ATTACK_AMMO2;
else if( !Q_stricmp( token, "laserdot" ))
II->attack2 = ATTACK_LASER_DOT;
else if( !Q_stricmp( token, "zoom" ))
II->attack2 = ATTACK_ZOOM;
else if( !Q_stricmp( token, "flashlight" ))
II->attack2 = ATTACK_FLASHLIGHT;
else if( !Q_stricmp( token, "switchmode" ))
II->attack2 = ATTACK_SWITCHMODE;
else if( !Q_stricmp( token, "swing" ))
II->attack2 = ATTACK_SWING;
else if( !Q_stricmp( token, "ironsight" ))
II->attack2 = ATTACK_IRONSIGHT;
else if( !Q_stricmp( token, "scope" ))
II->attack2 = ATTACK_SCOPE;
else II->attack1 = ALLOC_STRING( token ); // client command
}
else if( !Q_stricmp( token, "SpreadRange" ))
{
pfile = COM_ParseFile( pfile, token );
II->spread2[0].range = RandomRange( token );
}
else if( !Q_stricmp( token, "SpreadRangeIS" ))
{
pfile = COM_ParseFile( pfile, token );
II->spread2[1].range = RandomRange( token );
}
else if( !Q_stricmp( token, "SpreadType" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( "E_LINEAR", token ))
II->spread2[0].type = SPREAD_LINEAR;
else if( !Q_stricmp( "E_QUAD", token ))
II->spread2[0].type = SPREAD_QUAD;
else if( !Q_stricmp( "E_CUBE", token ))
II->spread2[0].type = SPREAD_CUBE;
else if( !Q_stricmp( "E_SQRT", token ))
II->spread2[0].type = SPREAD_SQRT;
else ALERT( at_warning, "ParseSecondaryAttack: unknown spread equalize type '%s'\n", token );
}
else if( !Q_stricmp( token, "SpreadTypeIS" ))
{
pfile = COM_ParseFile( pfile, token );
if( !Q_stricmp( "E_LINEAR", token ))
II->spread2[1].type = SPREAD_LINEAR;
else if( !Q_stricmp( "E_QUAD", token ))
II->spread2[1].type = SPREAD_QUAD;
else if( !Q_stricmp( "E_CUBE", token ))
II->spread2[1].type = SPREAD_CUBE;
else if( !Q_stricmp( "E_SQRT", token ))
II->spread2[1].type = SPREAD_SQRT;
else ALERT( at_warning, "ParseSecondaryAttack: unknown spread equalize type '%s'\n", token );
}
else if( !Q_stricmp( token, "SpreadExpand" ))
{
pfile = COM_ParseFile( pfile, token );
II->spread2[0].expand = Q_atof( token );
}
else if( !Q_stricmp( token, "SpreadExpandIS" ))
{
pfile = COM_ParseFile( pfile, token );
II->spread2[1].expand = Q_atof( token );
}
else if( !Q_stricmp( token, "PunchAngle" ))
{
pfile = ParseViewPunch( pfile, &II->feedback2[0] );
}
else if( !Q_stricmp( token, "PunchAngleIS" ))
{
pfile = ParseViewPunch( pfile, &II->feedback2[1] );
}
else if( !Q_stricmp( token, "recoil" ))
{
pfile = COM_ParseFile( pfile, token );
II->recoil2 = RandomRange( token );
}
else if( !Q_stricmp( token, "nextattack" ))
{
pfile = COM_ParseFile( pfile, token );
II->fNextAttack2 = Q_atof( token );
}
else if( !Q_stricmp( token, "SmashDecal" ))
{
pfile = COM_ParseFile( pfile, token );
II->smashDecals[1] = ALLOC_STRING( token );
}
pfile = COM_ParseFile( pfile, token );
}
return 1;
}
int CBasePlayerItem :: ParseSoundData( ItemInfo *II, char *pfile )
{
char token[256];
int i = 0;
int j = 0;
while( Q_stricmp( token, "SoundData" ))
{
if( !pfile ) return 0;
pfile = COM_ParseFile( pfile, token );
}
while( Q_stricmp( token, "{" ))
{
if( !pfile ) return 0;
pfile = COM_ParseFile( pfile, token );
}
pfile = COM_ParseFile( pfile, token );
while( Q_stricmp( token, "}" ))
{
if( !pfile ) return 0;
if( !Q_stricmp( token, "shootsound1" ))
{
pfile = COM_ParseFile( pfile, token );
if( II->sndcount1 < MAX_SHOOTSOUNDS )
{
II->shootsound1[II->sndcount1] = ALLOC_STRING( token );
PRECACHE_SOUND( STRING( II->shootsound1[II->sndcount1] ));
II->sndcount1++;
}
else ALERT( at_warning, "Too many shoot sounds for %s\n", STRING( pev->netname ));
}
else if ( !stricmp( token, "shootsound2" ))
{
pfile = COM_ParseFile( pfile, token );
if( II->sndcount2 < MAX_SHOOTSOUNDS )
{
II->shootsound2[II->sndcount2] = ALLOC_STRING( token );
PRECACHE_SOUND( STRING( II->shootsound2[II->sndcount2] ));
II->sndcount2++;
}
else ALERT( at_warning, "Too many shoot sounds for %s\n", STRING( pev->netname ));
}
else if( !stricmp( token, "emptysound" ))
{
pfile = COM_ParseFile( pfile, token );
if( II->emptysndcount < MAX_SHOOTSOUNDS )
{
II->emptysounds[II->emptysndcount] = ALLOC_STRING( token );
PRECACHE_SOUND( STRING( II->emptysounds[II->emptysndcount] ));
II->emptysndcount++;
}
else ALERT( at_warning, "Too many empty sounds for %s\n", STRING( pev->netname ));
}
pfile = COM_ParseFile( pfile, token );
}
return 1;
}
int CBasePlayerItem :: GetAnimation( Activity activity )
{
if( !FStrEq( STRING( pev->model ), STRING( iViewModel( ))))
return -1;
if( m_iIronSight )
{
// translate activity in case Iron Sight is enabled
Activity new_activity = GetIronSightActivity( activity );
// make sure what iron sight activity is found
int iAnim = LookupActivity( new_activity );
if( iAnim != -1 ) return iAnim;
// fallback to normal activity
}
return LookupActivity( activity );
}
int CBasePlayerItem :: SetAnimation( Activity activity, float fps )
{
int iSequence = GetAnimation( activity );
if( iSequence != -1 ) SendWeaponAnim( iSequence, fps );
else ALERT( at_aiconsole, "%s not found\n", activity_map[activity - 1].name );
return iSequence;
}
int CBasePlayerItem :: SetAnimation( char *name, float fps )
{
if( !FStrEq( STRING( pev->model ), STRING( iViewModel( ))))
return -1;
int iSequence = LookupSequence( name );
if( iSequence != -1 ) SendWeaponAnim( iSequence, fps );
else ALERT( at_aiconsole, "sequence \"%s\" not found\n", name );
return iSequence;
}
void CBasePlayerItem :: SendWeaponAnim( int iAnim, float framerate )
{
if( iAnim == -1 ) return; // sequence not found
m_pPlayer->pev->weaponanim = iAnim;
pev->framerate = framerate;
SetNextIdle( SequenceDuration( )); // auto-update idle activity
m_iClientAnim = -1; // force to send new sequence
}
Activity CBasePlayerItem :: GetIronSightActivity( Activity act )
{
switch( act )
{
case ACT_VM_IDLE: return ACT_VM_IDLE_IS;
case ACT_VM_IDLE_EMPTY: return ACT_VM_IDLE_EMPTY_IS;
case ACT_VM_RANGE_ATTACK: return ACT_VM_RANGE_ATTACK_IS;
case ACT_VM_MELEE_ATTACK: return ACT_VM_MELEE_ATTACK_IS;
case ACT_VM_SHOOT_LAST: return ACT_VM_SHOOT_LAST_IS;
case ACT_VM_LAST_MELEE_ATTACK: return ACT_VM_LAST_MELEE_ATTACK_IS;
case ACT_VM_SHOOT_EMPTY: return ACT_VM_SHOOT_EMPTY_IS;
case ACT_VM_RELOAD_EMPTY: return ACT_VM_RELOAD_EMPTY_IS;
case ACT_VM_PUMP_EMPTY: return ACT_VM_PUMP_EMPTY_IS;
case ACT_VM_RELOAD: return ACT_VM_RELOAD_IS;
case ACT_VM_PUMP: return ACT_VM_PUMP_IS;
}
return act; // default
}
void CBasePlayerItem :: ApplyPlayerSettings( bool bReset )
{
if (bReset)
{
// buz: Paranoia's speed adjustment
// return player speed to normal
m_pPlayer->pev->maxspeed = gSkillData.plrPrimaryMaxSpeed;
// buz: set jump force
m_pPlayer->SetJumpHeight( 100.0f );
}
else
{
// buz: Paranoia's speed adjustment
if (ClientMaxSpeed( )) m_pPlayer->pev->maxspeed = ClientMaxSpeed();
else m_pPlayer->pev->maxspeed = gSkillData.plrPrimaryMaxSpeed;
// buz: set jump force
if (ClientJumpHeight() != -1.0f)
m_pPlayer->SetJumpHeight( ClientJumpHeight( ));
else m_pPlayer->SetJumpHeight( 100.0f );
}
}
//=========================================================
// Spread system from Paranoia
//=========================================================
Vector CBasePlayerItem :: GetConeVectorForDegree( int degree )
{
switch( degree )
{
case 0: return g_vecZero;
case 1: return VECTOR_CONE_1DEGREES;
case 2: return VECTOR_CONE_2DEGREES;
case 3: return VECTOR_CONE_3DEGREES;
case 4: return VECTOR_CONE_4DEGREES;
case 5: return VECTOR_CONE_5DEGREES;
case 6: return VECTOR_CONE_6DEGREES;
case 7: return VECTOR_CONE_7DEGREES;
case 8: return VECTOR_CONE_8DEGREES;
case 9: return VECTOR_CONE_9DEGREES;
case 10: return VECTOR_CONE_10DEGREES;
case 15: return VECTOR_CONE_15DEGREES;
case 20: return VECTOR_CONE_20DEGREES;
}
return g_vecZero;
}
void CBasePlayerItem :: DoEqualizeSpread( int type, float &spread )
{
switch( type )
{
case SPREAD_QUAD:
spread = spread * spread;
break;
case SPREAD_CUBE:
spread = spread * spread * spread;
break;
case SPREAD_SQRT:
spread = sqrt( spread );
break;
}
}
// returns spread expand power [0..1] based on current time
float CBasePlayerItem :: CalcSpread( void )
{
float decay = ( gpGlobals->time - m_flLastShotTime ) / ( m_flSpreadTime * m_flLastSpreadPower );
if( decay > 1.0f ) return 0.0f;
return ( 1.0f - decay ) * m_flLastSpreadPower;
}
float CBasePlayerItem :: ExpandSpread( float expandPower )
{
// buz: in ducking more accuracy
// g-cont. make sure what is not a duck in the jump
if( FBitSet( m_pPlayer->pev->flags, FL_DUCKING ) && FBitSet( pev->flags, FL_ONGROUND ))
expandPower *= 0.7f;
float curspread = CalcSpread();
m_flLastShotTime = gpGlobals->time;
m_flLastSpreadPower = curspread + expandPower;
if( m_flLastSpreadPower > 2.0f )
m_flLastSpreadPower = 2.0f;
return curspread;
}
Vector CBasePlayerItem :: GetSpreadVec( void )
{
const spread_t *info = pSpread1(); // auto-select between normal and IronSight settings
float spread = CalcSpread();
DoEqualizeSpread( info->type, spread );
Vector minspread = GetConeVectorForDegree( (int)info->range.m_flMin );
Vector addspread = GetConeVectorForDegree( (int)info->range.m_flMax );
Vector vecSpread = minspread + ( addspread * spread );
vecSpread.z = spread; // scale
return vecSpread;
}
Vector CBasePlayerItem :: CalcSpreadVec( const spread_t *info, float &spread )
{
spread = ExpandSpread( info->expand );
DoEqualizeSpread( info->type, spread );
Vector minspread = GetConeVectorForDegree( (int)info->range.m_flMin );
Vector addspread = GetConeVectorForDegree( (int)info->range.m_flMax );
return ( minspread + ( addspread * spread ));
}
void CBasePlayerItem :: PlayerJump( void )
{
ExpandSpread( 0.7f );
}
void CBasePlayerItem :: PlayerRun( void )
{
ExpandSpread( 0.1f );
}
void CBasePlayerItem :: PlayerWalk( void )
{
ExpandSpread( 0.03f );
}
//=========================================================
// Zoom In\Out
//=========================================================
void CBasePlayerItem :: ZoomUpdate( void )
{
BOOL m_bUseZoom = FALSE;
if( iAttack1() == ATTACK_ZOOM && FBitSet( m_pPlayer->pev->button, IN_ATTACK ))
m_bUseZoom = TRUE;
if( iAttack2() == ATTACK_ZOOM && FBitSet( m_pPlayer->pev->button, IN_ATTACK2 ))
m_bUseZoom = TRUE;
if( m_bUseZoom )
{
if( m_iZoom == NOT_IN_ZOOM )
{
if( m_flHoldTime > UTIL_WeaponTimeBase( ))
return;
EMIT_SOUND( ENT( m_pPlayer->pev ), CHAN_ITEM, "weapons/zoom.wav", 1, ATTN_NORM );
m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.8;
m_iZoom = NORMAL_ZOOM;
}
if( m_iZoom == NORMAL_ZOOM )
{
m_pPlayer->m_iFOV = ZOOM_DEFAULT; // FIXME: get zoom values from weapon settings
m_pPlayer->pev->viewmodel = iStringNull;
m_iZoom = UPDATE_ZOOM; // ready to zooming, wait for 0.8 secs
}
if( m_iZoom == UPDATE_ZOOM && m_pPlayer->m_iFOV > ZOOM_MAXIMUM )
{
if( m_flTimeUpdate < UTIL_WeaponTimeBase( ))
{
m_flTimeUpdate = UTIL_WeaponTimeBase() + 0.02f;
m_pPlayer->m_iFOV--;
}
}
if( m_iZoom == RESET_ZOOM )
ZoomReset();
}
else if( m_iZoom > NORMAL_ZOOM )
{
m_iZoom = RESET_ZOOM;
}
#if 0
MESSAGE_BEGIN( MSG_ONE, gmsgZoomHUD, NULL, m_pPlayer->pev );
WRITE_BYTE( m_iZoom );
MESSAGE_END();
#endif
}
void CBasePlayerItem :: ZoomReset( void )
{
// return viewmodel
if( m_iZoom != NOT_IN_ZOOM )
{
m_pPlayer->pev->viewmodel = iViewModel();
m_pPlayer->pev->iuser3 = iHandModel();
m_flHoldTime = UTIL_WeaponTimeBase() + 0.5;
m_pPlayer->m_iFOV = 90;
m_iZoom = NOT_IN_ZOOM; // clear zoom
#if 0
MESSAGE_BEGIN( MSG_ONE, gmsgZoomHUD, NULL, m_pPlayer->pev );
WRITE_BYTE( m_iZoom );
MESSAGE_END();
#endif
// update client data manually
m_pPlayer->UpdateClientData();
}
}
//=========================================================
// generic base functions
//=========================================================
BOOL CBasePlayerItem :: DefaultDeploy( Activity sequence )
{
m_iClientAnim = -1;
m_iClientSkin = -1;
m_iClientBody = -1;
// init spread system
m_flSpreadTime = fSpreadTime();
m_flLastSpreadPower = 0.0f;
m_flLastShotTime = 0.0f;
if( PLAYER_HAS_SUIT )
pev->body |= MILITARY_SUIT;
else pev->body &= ~MILITARY_SUIT;
m_pPlayer->pev->viewmodel = iViewModel();
m_pPlayer->pev->iuser3 = iHandModel();
m_pPlayer->pev->weaponmodel = iWorldModel();
Q_strncpy( m_pPlayer->m_szAnimExtention, szAnimExt(), sizeof( m_pPlayer->m_szAnimExtention ));
SET_MODEL( edict(), STRING( iViewModel( )));
float fps = 1.0f;
if( g_pGameRules->IsMultiplayer( ))
fps *= 1.5f; // speed up 1.5x
if ( SetAnimation( sequence, fps ) != -1 )
{
// make some delay before idle playing
SetNextAttack( SequenceDuration() - 0.1f );
return TRUE;
}
// animation missed
return FALSE;
}
BOOL CBasePlayerItem :: DefaultHolster( Activity sequence, bool force )
{
m_fInReload = FALSE;
int iResult = 0;
if( m_pSpot )
{
// disable laser dot
EMIT_SOUND( m_pPlayer->edict(), CHAN_ITEM, "weapons/spot_off.wav", 1, ATTN_NORM );
m_pSpot->Killed( NULL, GIB_NEVER );
m_pSpot = NULL;
}
m_iSkin = 0; // reset screen
ZoomReset();
if( iAttack1() == ATTACK_FLASHLIGHT || iAttack2() == ATTACK_FLASHLIGHT )
ClearBits( m_pPlayer->pev->effects, EF_DIMLIGHT ); // FIXME: create new flag for weapon flashlight
// disbale IronSight before switching to next weapon
if( m_iIronSight )
{
m_iIronSight = 0;
// can play full animation
if( !force )
{
int iAnim = -1;
if( !HasAmmo( )) iAnim = GetAnimation( ACT_VM_IRONSIGHT_OFF_EMPTY );
if( iAnim == -1 ) iAnim = GetAnimation( ACT_VM_IRONSIGHT_OFF );
SendWeaponAnim( iAnim );
SetNextAttack( SequenceDuration() + 0.1f );
m_fWaitForHolster = TRUE; // queue enabled
return 0;
}
}
// disable queue
m_fWaitForHolster = FALSE;
float fps = 1.0f;
if( g_pGameRules->IsMultiplayer( ))
fps *= 2.5f;
iResult = SetAnimation( sequence, fps );
if( iResult != -1 )
{
// delay before switching
SetNextAttack( SequenceDuration() + 0.1f );
iResult = 1;
}
if( FBitSet( iFlags(), ITEM_FLAG_EXHAUSTIBLE ) && !m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] && !m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] )
{
// no more ammo!
ClearBits( m_pPlayer->pev->weapons, BIT( m_iId ));
m_pPlayer->pev->viewmodel = iStringNull;
m_pPlayer->pev->weaponmodel = iStringNull;
SetThink( DestroyItem );
SetNextThink( 0.5 );
}
// animation not found
return iResult;
}
void CBasePlayerItem :: DefaultIdle( void )
{
// weapon have clip and ammo or just have ammo
if(( iMaxClip() && m_iClip ) || m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0 || iMaxAmmo1() == -1 )
{
// play random idle animation
float flRand = RANDOM_FLOAT( 0, 1.0f );
if( flRand < 0.5f ) SetAnimation( ACT_VM_IDLE );
else m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT( 10.0f, 15.0f );
}
else
{
SetAnimation( ACT_VM_IDLE_EMPTY );
}
}
BOOL CBasePlayerItem :: DefaultReload( Activity sequence )
{
if( m_flNextPrimaryAttack > UTIL_WeaponTimeBase( ))
return FALSE;
if( m_flNextSecondaryAttack > UTIL_WeaponTimeBase( ))
return FALSE;
if( m_cActiveRockets && m_iSpot )
{
// no reloading when there are active missiles tracking the designator.
// ward off future autoreload attempts by setting next attack time into the future for a bit.
SetNextAttack( 0.5f );
return FALSE;
}
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0 ) // have ammo?
{
if( iMaxClip() == m_iClip )
return FALSE;
if( m_iStepReload == NOT_IN_RELOAD )
{
if( SetAnimation( ACT_VM_START_RELOAD ) != -1 )
{
// found anim, continue
m_iStepReload = START_RELOAD;
m_fInReload = TRUE; // disable reload button
return TRUE; // start reload cycle. See also ItemPostFrame
}
else // init default reload
{
int i = Q_min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] );
if( i == 0 ) return FALSE;
int iResult = -1;
ZoomReset(); // reset zoom
if( m_iClip <= 0 ) // empty clip ?
{
// iResult is error code
iResult = SetAnimation( ACT_VM_RELOAD_EMPTY );
m_iStepReload = EMPTY_RELOAD; // it's empty reload
}
if( iResult == -1 )
{
SetAnimation( sequence );
m_iStepReload = NORMAL_RELOAD; // not empty reload or sequence not found
}
if( m_pSpot ) m_pSpot->Suspend( SequenceDuration( )); // suspend laserdot
SetNextAttack( SequenceDuration( ));
m_fInReload = TRUE; // disable reload button
return TRUE;
}
}
else if( m_iStepReload == START_RELOAD )
{
// continue step reload
if( m_flTimeWeaponIdle > UTIL_WeaponTimeBase( ))
return FALSE;
// was waiting for gun to move to side
SetAnimation( sequence );
m_iStepReload = CONTINUE_RELOAD;
}
else if( m_iStepReload == CONTINUE_RELOAD )
{
// Add them to the clip
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]--;
m_iStepReload = START_RELOAD;
m_iClip++;
if( m_iClip == iMaxClip( ))
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.1;
}
return TRUE;
}
return FALSE;
}
BOOL CBasePlayerItem :: DefaultSwing( int primary )
{
int fDidHit = FALSE;
TraceResult tr;
UTIL_MakeVectors( m_pPlayer->pev->v_angle );
Vector vecSrc = m_pPlayer->GetGunPosition();
Vector vecEnd = vecSrc + gpGlobals->v_forward * 32.0f;
UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, ENT( m_pPlayer->pev ), &tr );
if ( tr.flFraction >= 1.0 )
{
UTIL_TraceHull( vecSrc, vecEnd, dont_ignore_monsters, head_hull, m_pPlayer->edict(), &tr );
if ( tr.flFraction < 1.0 )
{
// Calculate the point of intersection of the line (or hull) and the object we hit
// This is and approximation of the "best" intersection
CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit );
if ( !pHit || pHit->IsBSPModel() )
UTIL_FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, m_pPlayer->edict() );
vecEnd = tr.vecEndPos; // This is the point on the actual surface (the hull could have hit space)
}
}
// player "shoot" animation
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
if ( tr.flFraction >= 1.0f )
{
if( emptycnt( ))
EMIT_SOUND( m_pPlayer->edict(), CHAN_WEAPON, STRING( EmptySnd( )), 1.0, ATTN_NORM );
if (primary)
{
// HACKHACK: ACT_VM_SHOOT_EMPTY_IS as miss attack for knife
if( LookupActivity( ACT_VM_SHOOT_EMPTY_IS ) != ACTIVITY_NOT_AVAILABLE )
SetAnimation( ACT_VM_SHOOT_EMPTY_IS );
else SetAnimation( ACT_VM_RANGE_ATTACK );
}
else SetAnimation( ACT_VM_SHOOT_EMPTY );
}
else
{
if (primary) SetAnimation( ACT_VM_RANGE_ATTACK );
else SetAnimation( ACT_VM_MELEE_ATTACK );
// hit
fDidHit = TRUE;
CBaseEntity *pEntity = CBaseEntity::Instance(tr.pHit);
ClearMultiDamage( );
// Wargon: Исправлено гибание ножем.
if( primary ) pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgCrowbar, gpGlobals->v_forward, &tr, DMG_CLUB | DMG_NEVERGIB );
else pEntity->TraceAttack(m_pPlayer->pev, gSkillData.plrDmgCrowbarSec, gpGlobals->v_forward, &tr, DMG_CLUB | DMG_NEVERGIB );
ApplyMultiDamage( m_pPlayer->pev, m_pPlayer->pev );
// play thwack, smack, or dong sound
float flVol = 1.0f;
int fHitWorld = TRUE;
if (pEntity)
{
if ( pEntity->Classify() != CLASS_NONE && pEntity->Classify() != CLASS_MACHINE )
{
if( primary )
{
if( sndcnt1( ))
EMIT_SOUND( m_pPlayer->edict(), CHAN_WEAPON, STRING( ShootSnd1( )), 1.0, ATTN_NORM );
}
else
{
// FIXME: hardcoded sound
EMIT_SOUND(ENT(m_pPlayer->pev), CHAN_ITEM, "weapons/knife_stab.wav", 1, ATTN_NORM);
}
m_pPlayer->m_iWeaponVolume = KNIFE_BODYHIT_VOLUME;
if ( !pEntity->IsAlive() )
return TRUE;
else flVol = 0.1;
fHitWorld = FALSE;
}
}
// play texture hit sound
// UNDONE: Calculate the correct point of intersection when we hit with the hull instead of the line
if (fHitWorld)
{
float fvolbar = TEXTURETYPE_PlaySound( &tr, vecSrc, vecSrc + (vecEnd-vecSrc) * 2.0f, BULLET_STAB );
if( sndcnt2( ))
{
const char *pszSound = STRING( ShootSnd2());
EMIT_SOUND_DYN( m_pPlayer->edict(), CHAN_WEAPON, pszSound, fvolbar, ATTN_NORM, 0, RANDOM_LONG( 98, 102 ));
}
}
// delay the decal a bit
m_trHit = tr;
m_pPlayer->m_iWeaponVolume = flVol * KNIFE_WALLHIT_VOLUME;
if (primary)
{
SetThink( &CBasePlayerItem :: KnifeDecal1 );
SetNextThink( 0.1f );
}
else
{
SetThink( &CBasePlayerItem :: KnifeDecal2 );
SetNextThink( 0.15f );
}
}
return fDidHit;
}
BOOL CBasePlayerItem :: PlayEmptySound( void )
{
if( m_iPlayEmptySound )
{
if( emptycnt( ))
EMIT_SOUND( m_pPlayer->edict(), CHAN_WEAPON, STRING( EmptySnd( )), 0.8, ATTN_NORM );
m_iPlayEmptySound = 0;
return TRUE;
}
return FALSE;
}
void CBasePlayerItem :: ResetEmptySound( void )
{
m_iPlayEmptySound = 1;
}
void CBasePlayerItem :: PlayAttackSound( int primary )
{
if( primary )
{
if( sndcnt1( )) EMIT_SOUND( m_pPlayer->edict(), CHAN_WEAPON, STRING( ShootSnd1( )), 1.0, ATTN_NORM );
}
else
{
if( sndcnt2( )) EMIT_SOUND( m_pPlayer->edict(), CHAN_WEAPON, STRING( ShootSnd2( )), 1.0, ATTN_NORM );
}
}
int CBasePlayerItem :: GetCurrentAttack( const char *ammo, int primary )
{
AmmoInfo *pAmmo = UTIL_FindAmmoType( ammo );
int iResult;
if( !pAmmo || !Q_stricmp( ammo, "none" )) // no ammo or ammo not found
{
// just play animation and sound
if( primary ) iResult = ( SetAnimation( ACT_VM_RANGE_ATTACK ) == -1 ) ? 0 : 1;
else iResult = ( SetAnimation( ACT_VM_MELEE_ATTACK ) == -1 ) ? 0 : 1;
PlayAttackSound( primary );
SetPlayerEffects();
}
else if( pAmmo->iMissileClassName != iStringNull )
{
// missile attack (throw entity)
iResult = ThrowGeneric( ammo, primary );
}
else
{
// shoot bullets (default case)
iResult = ShootGeneric( ammo, primary );
}
return iResult;
}
int CBasePlayerItem :: PlayCurrentAttack( int action, int primary )
{
int iResult = 0;
if( action == ATTACK_ZOOM )
iResult = 1; // See void ZoomUpdate( void ); for details
else if( action == ATTACK_AMMO1 )
iResult = GetCurrentAttack( pszAmmo1(), primary );
else if( action == ATTACK_AMMO2 )
iResult = GetCurrentAttack( pszAmmo2(), primary );
else if( action == ATTACK_LASER_DOT )
{
m_iSpot = !m_iSpot;
if( !m_iSpot && m_pSpot )
{
// disable laser dot
EMIT_SOUND( ENT( m_pPlayer->pev ), CHAN_ITEM, "weapons/spot_off.wav", 1, ATTN_NORM );
m_pSpot->Killed( NULL, GIB_NEVER );
m_pSpot = NULL;
m_iSkin = 0; // disable screen
}
iResult = 1;
}
else if( action == ATTACK_FLASHLIGHT )
{
if( FBitSet( m_pPlayer->pev->effects, EF_DIMLIGHT ))
{
ClearBits( m_pPlayer->pev->effects, EF_DIMLIGHT );
EMIT_SOUND_DYN( ENT( m_pPlayer->pev ), CHAN_WEAPON, SOUND_FLASHLIGHT_OFF, 1.0, ATTN_NORM, 0, PITCH_NORM );
}
else
{
SetBits( m_pPlayer->pev->effects, EF_DIMLIGHT );
EMIT_SOUND_DYN( ENT( m_pPlayer->pev ), CHAN_WEAPON, SOUND_FLASHLIGHT_ON, 1.0, ATTN_NORM, 0, PITCH_NORM );
}
iResult = 1;
}
else if( action == ATTACK_SWITCHMODE )
{
if( !m_iBody )
{
m_iBody = 1;
pev->button = 1; // enable custom firemode
SetAnimation( ACT_VM_TURNON );
}
else
{
SetAnimation( ACT_VM_TURNOFF );
pev->button = 0; // disable custom firemode
m_iClientBody = m_iBody = 0; // make delay before change
}
iResult = 1;
}
else if( action == ATTACK_SWING )
{
iResult = DefaultSwing( primary );
}
else if( action == ATTACK_IRONSIGHT )
{
int iAnim = -1;
if( !m_iIronSight )
{
if( !HasAmmo( )) iAnim = GetAnimation( ACT_VM_IRONSIGHT_ON_EMPTY );
if( iAnim == -1 ) iAnim = GetAnimation( ACT_VM_IRONSIGHT_ON );
m_iIronSight = 1;
}
else
{
if( !HasAmmo( )) iAnim = GetAnimation( ACT_VM_IRONSIGHT_OFF_EMPTY );
if( iAnim == -1 ) iAnim = GetAnimation( ACT_VM_IRONSIGHT_OFF );
m_iIronSight = 0;
}
ApplyPlayerSettings( FALSE );
SendWeaponAnim( iAnim );
iResult = 1;
}
else if( action == ATTACK_SCOPE )
{
int iAnim = -1;
if( m_pPlayer->m_iGasMaskOn )
{
UTIL_ShowMessage( "#GAS_AND_SCOPE", m_pPlayer );
SetNextAttack( 0.5f );
return -1;
}
else if( m_pPlayer->m_iHeadShieldOn )
{
UTIL_ShowMessage( "#SCOPE_AND_SHIELD", m_pPlayer );
SetNextAttack( 0.5f );
return -1;
}
if( !m_iIronSight )
{
if( !HasAmmo( )) iAnim = GetAnimation( ACT_VM_IRONSIGHT_ON_EMPTY );
if( iAnim == -1 ) iAnim = GetAnimation( ACT_VM_IRONSIGHT_ON );
m_iIronSight = 1;
}
else
{
if( !HasAmmo( )) iAnim = GetAnimation( ACT_VM_IRONSIGHT_OFF_EMPTY );
if( iAnim == -1 ) iAnim = GetAnimation( ACT_VM_IRONSIGHT_OFF );
m_iIronSight = 0;
}
ApplyPlayerSettings( FALSE );
SendWeaponAnim( iAnim );
iResult = 1;
}
else if( action == ATTACK_NONE )
{
return -1;
}
else
{
// just command
char command[64];
if( !strncmp( STRING( action ), "fire ", 5 ))
{
char *target = (char *)STRING( action );
target = target + 5; // remove "fire "
FireTargets( target, m_pPlayer, this, USE_TOGGLE, 1.0f ); // activate target
}
else
{
Q_snprintf( command, sizeof( command ), "%s\n", STRING( action ));
SERVER_COMMAND( command );
}
// just play animation and sound
if( primary ) SetAnimation( ACT_VM_RANGE_ATTACK );
else SetAnimation( ACT_VM_MELEE_ATTACK );
m_pPlayer->SetAnimation( PLAYER_ATTACK1 );
PlayAttackSound( primary );
}
return iResult;
}
//=========================================================
// Get Atatck Info by AmmoInfo
//=========================================================
int CBasePlayerItem :: GetAmmoType( const char *ammo )
{
if( !Q_stricmp( ammo, pszAmmo1() ))
return AMMO_PRIMARY; // primary ammo
if( !Q_stricmp( ammo, pszAmmo2() ))
return AMMO_SECONDARY; // secondary ammo
return AMMO_UNKNOWN; // no ammo
}
int CBasePlayerItem :: UseAmmo( const char *ammo, int count )
{
int ammoType = GetAmmoType( ammo );
if( ammoType == AMMO_UNKNOWN )
{
return -1;
}
else if( ammoType == AMMO_PRIMARY )
{
if( iMaxClip() == -1 && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] > 0 )
{
// noclip
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] < count )
count = 1;
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= count;
}
else if( m_iClip > 0 )
{
// have clip
if( m_iClip < count )
count = 1;
m_iClip -= count;
}
else
{
// ammo is out
return 0;
}
}
else if( ammoType == AMMO_SECONDARY )
{
if( m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] > 0 )
{
if( m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] < count )
count = 1;
m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] -= count;
}
else
{
// ammo is out
return 0;
}
}
return 1;
}
// client effects (muzzleflash, decals, shell, etc)
void CBasePlayerItem :: PlayClientFire( const Vector &vecDir, float spread, int iAnim, int shellidx, string_t shootsnd, int cShots )
{
int soundidx = 0;
// params sent table:
// args->fparam1 = vecDir.x
// args->fparam2 = vecDir.y
// args->iparam1 = iAnim
// args->iparam2 = spread * 255
// args->bparam1 = shellindex (bool expanded up to 16 bits)
// args->bparam2 = soundindex (bool expanded up to 16 bits)
if( shootsnd != iStringNull )
soundidx = PRECACHE_SOUND( STRING( shootsnd ));
PLAYBACK_EVENT_FULL( 0, m_pPlayer->edict(), g_usShootEvent, 0.0, (float *)&g_vecZero, (float *)&g_vecZero,
vecDir.x, vecDir.y, (iAnim & 0x0FFF)|((cShots & 0xF )<<12 ), (int)( spread * 255 ), shellidx, soundidx );
}
void CBasePlayerItem :: SetPlayerEffects( void )
{
m_pPlayer->m_iWeaponVolume = iVolume();
m_pPlayer->m_iWeaponFlash = iFlash();
if( m_pPlayer->m_iWeaponFlash )
SetBits( m_pPlayer->pev->effects, EF_MUZZLEFLASH );
m_pPlayer->SetAnimation( PLAYER_ATTACK1 ); // player animation
}
float CBasePlayerItem :: AutoAimDelta( int primary )
{
const spread_t *info = (primary) ? pSpread1() : pSpread2();
return GetConeVectorForDegree( (int)info->range.m_flMax ).x;
}
int CBasePlayerItem :: ShootGeneric( const char *ammo, int primary, int cShots )
{
if( m_pPlayer->pev->waterlevel != 3 || FBitSet( iFlags(), ITEM_FLAG_SHOOT_UNDERWATER ))
{
// have ammo and player not underwater
if( !UseAmmo( ammo, cShots ))
return 0;
SetPlayerEffects();
// viewmodel animation
ZoomReset();
int iAnim = -1;
if( primary )
{
if( iMaxClip() && !m_iClip )
iAnim = GetAnimation( ACT_VM_SHOOT_LAST );
if( iAnim == -1 )
iAnim = GetAnimation( ACT_VM_RANGE_ATTACK );
}
else
{
if( !m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] )
iAnim = GetAnimation( ACT_VM_LAST_MELEE_ATTACK );
if( iAnim == -1 )
iAnim = GetAnimation( ACT_VM_MELEE_ATTACK );
}
float spread, flDistance = 8192.0f; // set max distance
Vector vecSrc = m_pPlayer->GetGunPosition();
Vector vecAiming = gpGlobals->v_forward;
Vector vecSpread = CalcSpreadVec((primary) ? pSpread1() : pSpread2(), spread );
if( FBitSet( iFlags(), ITEM_FLAG_USEAUTOAIM ))
{
vecAiming = m_pPlayer->GetAutoaimVector( AutoAimDelta( primary ));
}
else
{
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
vecAiming = gpGlobals->v_forward;
}
AmmoInfo *pInfo = UTIL_FindAmmoType( ammo );
int shellIndex = 0;
float flDamage = 0.0f;
int numShots = 1;
if( pInfo != NULL )
{
shellIndex = pInfo->iShellIndex;
flDamage = pInfo->flPlayerDamage;
flDistance = pInfo->flDistance;
numShots = pInfo->iNumShots;
}
Vector vecDir = m_pPlayer->FireBulletsPlayer( cShots * numShots, vecSrc, vecAiming, vecSpread, flDistance, flDamage,
m_pPlayer->pev, m_pPlayer->random_seed );
PlayClientFire( vecDir, spread, iAnim, shellIndex, (primary) ? ShootSnd1() : ShootSnd2( ), cShots * numShots );
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + RANDOM_FLOAT ( 10, 15 );
return 1;
}
return 0;
}
int CBasePlayerItem :: ThrowGeneric( const char *ammo, int primary, int cShots )
{
// have ammo and player not underwater
if( !UseAmmo( ammo, cShots ))
return 0;
SetPlayerEffects();
// viewmodel animation
ZoomReset();
int iAnim = -1;
if( primary )
{
if( iMaxClip() && !m_iClip )
iAnim = GetAnimation( ACT_VM_SHOOT_LAST );
if( iAnim == -1 )
iAnim = GetAnimation( ACT_VM_RANGE_ATTACK );
if( sndcnt1( ))
EMIT_SOUND( m_pPlayer->edict(), CHAN_WEAPON, STRING( ShootSnd1( )), 1.0, ATTN_NORM );
}
else
{
if( !m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] )
iAnim = GetAnimation( ACT_VM_LAST_MELEE_ATTACK );
if( iAnim == -1 )
iAnim = GetAnimation( ACT_VM_MELEE_ATTACK );
if( sndcnt2( ))
EMIT_SOUND( m_pPlayer->edict(), CHAN_WEAPON, STRING( ShootSnd2( )), 1.0, ATTN_NORM );
}
SendWeaponAnim( iAnim );
AmmoInfo *pInfo = UTIL_FindAmmoType( ammo );
if( !pInfo ) return -1; // ammo not found
float flDamage = pInfo->flPlayerDamage;
float flDistance = pInfo->flDistance;
float spread;
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
#if 1
Vector vecSrc = m_pPlayer->GetGunPosition() + gpGlobals->v_forward * vecThrowOffset().x
+ gpGlobals->v_right * vecThrowOffset().y + gpGlobals->v_up * vecThrowOffset().z;
#else
Vector vecSrc = m_pPlayer->GetGunPosition() + gpGlobals->v_forward * weapon_x.value
+ gpGlobals->v_right * weapon_y.value + gpGlobals->v_up * weapon_z.value;
#endif
Vector vecAiming = gpGlobals->v_forward;
Vector vecSpread = CalcSpreadVec((primary) ? pSpread1() : pSpread2(), spread );
if( FBitSet( iFlags(), ITEM_FLAG_USEAUTOAIM ))
{
vecAiming = m_pPlayer->GetAutoaimVector( AutoAimDelta( primary ));
}
else
{
UTIL_MakeVectors( m_pPlayer->pev->v_angle + m_pPlayer->pev->punchangle );
vecAiming = gpGlobals->v_forward;
}
// FIXME: this is a limitation of weapon system. Make generic tuneable projectile someday...
if( FStrEq( STRING( pInfo->iMissileClassName ), "rpg_rocket" ))
{
CRpgRocket *pRocket = CRpgRocket :: CreateRpgRocket( vecSrc, m_pPlayer->pev->v_angle, m_pPlayer, this );
pRocket->pev->velocity = vecAiming * flDistance;
pRocket->pev->velocity += vecAiming * DotProduct( m_pPlayer->pev->velocity, vecAiming );
pRocket->pev->dmg = flDamage;
return 1;
}
else if( FStrEq( STRING( pInfo->iMissileClassName ), "grenade_vog25" ))
{
CGrenade *pGrenade = CGrenade :: ShootGeneric( m_pPlayer, vecSrc, vecAiming * flDistance, pInfo->iMissileClassName );
pGrenade->pev->gravity = 0.5;// lower gravity since grenade is aerodynamic and engine doesn't know it.
pGrenade->pev->dmg = flDamage;
return 1;
}
return 0;
}
//=========================================================
// called by the new item with the existing item as parameter
//
// if we call ExtractAmmo(), it's because the player is picking up this type of weapon for
// the first time. If it is spawned by the world, m_iDefaultAmmo will have a default ammo amount in it.
// if this is a weapon dropped by a dying player, has 0 m_iDefaultAmmo, which means only the ammo in
// the weapon clip comes along.
//=========================================================
int CBasePlayerItem :: ExtractAmmo( CBasePlayerItem *pWeapon, BOOL duplicate )
{
int iResult = 0;
if( pszAmmo1( ))
{
iResult |= pWeapon->AddPrimaryAmmo( m_iDefaultAmmo1, pszAmmo1(), iMaxClip(), iMaxAmmo1(), duplicate );
m_iDefaultAmmo1 = 0;
}
if( pszAmmo2( ))
{
iResult |= pWeapon->AddSecondaryAmmo( m_iDefaultAmmo2, pszAmmo2(), iMaxAmmo2() );
m_iDefaultAmmo2 = 0;
}
return iResult;
}
//=========================================================
// called by the new item's class with the existing item as parameter
//=========================================================
int CBasePlayerItem :: ExtractClipAmmo( CBasePlayerItem *pWeapon )
{
int iAmmo;
if( m_iClip == WEAPON_NOCLIP )
iAmmo = 0; // guns with no clips always come empty if they are second-hand
else iAmmo = m_iClip;
return pWeapon->m_pPlayer->GiveAmmo( iAmmo, pszAmmo1()); // , &m_iPrimaryAmmoType
}
BOOL CBasePlayerItem :: AddPrimaryAmmo( int iCount, const char *szName, int iMaxClip, int iMaxCarry, BOOL duplicate )
{
int iIdAmmo;
if( iMaxClip < 1 )
{
m_iClip = WEAPON_NOCLIP;
iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName );
}
else if( m_iClip == 0 && duplicate == FALSE )
{
int i = Q_min( m_iClip + iCount, iMaxClip ) - m_iClip;
m_iClip += i;
iIdAmmo = m_pPlayer->GiveAmmo( iCount - i, szName );
}
else
{
iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName );
}
if( iIdAmmo > 0 )
{
m_iPrimaryAmmoType = iIdAmmo;
if( m_pPlayer->HasPlayerItem( this ))
{
// play the "got ammo" sound only if we gave some ammo to a player that already had this gun.
// if the player is just getting this gun for the first time, DefaultTouch will play the "picked up gun" sound for us.
EMIT_SOUND( edict(), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM );
}
}
return iIdAmmo > 0 ? TRUE : FALSE;
}
BOOL CBasePlayerItem :: AddSecondaryAmmo( int iCount, const char *szName, int iMax )
{
int iIdAmmo;
iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName );
//m_pPlayer->m_rgAmmo[m_iSecondaryAmmoType] = iMax; // hack for testing
if (iIdAmmo > 0)
{
m_iSecondaryAmmoType = iIdAmmo;
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM);
}
return iIdAmmo > 0 ? TRUE : FALSE;
}
// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal
int CBasePlayerItem :: AddDuplicate( CBasePlayerItem *pOriginal )
{
if( FBitSet( iFlags(), ITEM_FLAG_NODUPLICATE ))
return FALSE;
if( m_iDefaultAmmo1 || m_iDefaultAmmo2 )
return ExtractAmmo( pOriginal, TRUE );
return ExtractClipAmmo( pOriginal );
}
void CBasePlayerItem :: ItemPostFrame( void )
{
if( m_iSpot && !m_pSpot )
{
// enable laser dot
m_pSpot = CLaserSpot :: CreateSpot();
EMIT_SOUND( ENT( m_pPlayer->pev ), CHAN_ITEM, "weapons/spot_on.wav", 1, ATTN_NORM );
}
if( !m_iSpot && m_pSpot )
{
// disable laser dot
EMIT_SOUND( ENT( m_pPlayer->pev ), CHAN_ITEM, "weapons/spot_off.wav", 1, ATTN_NORM );
m_pSpot->Killed( NULL, GIB_NEVER );
m_pSpot = NULL;
}
if( m_pSpot )
m_pSpot->Update( m_pPlayer );
ZoomUpdate(); // update zoom
if( m_iStepReload == NOT_IN_RELOAD )
m_fInReload = FALSE; // finished
if( m_flTimeUpdate < UTIL_WeaponTimeBase( ))
PostIdle(); // catch all
if( m_fInReload && m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase( ))
{
if( m_iStepReload > CONTINUE_RELOAD )
{
// complete the reload.
int j = Q_min( iMaxClip() - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType]);
// Add them to the clip
m_iClip += j;
m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] -= j;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 0.01; // play PostReload
m_fInReload = FALSE;
}
}
if( FBitSet( m_pPlayer->pev->button, IN_ATTACK2 ) && ( m_flNextSecondaryAttack <= gpGlobals->time ))
{
SecondaryAttack();
// ClearBits( m_pPlayer->pev->button, IN_ATTACK2 );
}
else if( FBitSet( m_pPlayer->pev->button, IN_ATTACK ) && ( m_flNextPrimaryAttack <= gpGlobals->time ))
{
PrimaryAttack();
// ClearBits( m_pPlayer->pev->button, IN_ATTACK );
}
else if( FBitSet( m_pPlayer->pev->button, IN_RELOAD ) && iMaxClip() != WEAPON_NOCLIP && !m_fInReload )
{
// reload when reload is pressed, or if no buttons are down and weapon is empty.
Reload();
}
else if( !FBitSet( m_pPlayer->pev->button, IN_ATTACK | IN_ATTACK2 ))
{
// play sequence holster / deploy if player find or lose suit
if(( PLAYER_HAS_SUIT && !PLAYER_DRAW_SUIT ) || ( !PLAYER_HAS_SUIT && PLAYER_DRAW_SUIT ))
{
m_pPlayer->m_pActiveItem->Holster( true );
m_pPlayer->QueueItem( this );
if( m_pPlayer->m_pActiveItem )
m_pPlayer->m_pActiveItem->Deploy();
}
if( !CanDeploy() && m_flNextPrimaryAttack < gpGlobals->time )
{
// weapon isn't useable, switch.
if( !FBitSet( iFlags(), ITEM_FLAG_NOAUTOSWITCHEMPTY ) && g_pGameRules->GetNextBestWeapon( m_pPlayer, this ))
{
m_flNextPrimaryAttack = gpGlobals->time + 0.3;
return;
}
}
else
{
// weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
if( m_iClip == 0 && !FBitSet( iFlags(), ITEM_FLAG_NOAUTORELOAD ) && m_flNextPrimaryAttack < gpGlobals->time )
{
Reload();
return;
}
}
m_iPlayEmptySound = 1; // reset empty sound
if( FBitSet( iFlags(), ITEM_FLAG_USEAUTOAIM ))
m_pPlayer->GetAutoaimVector( AutoAimDelta( TRUE ));
if( m_flTimeWeaponIdle < UTIL_WeaponTimeBase( ))
{
// step reload
if( m_iStepReload > NOT_IN_RELOAD )
{
if( m_iClip != iMaxClip() && m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] )
{
Reload();
}
else
{
// reload debounce has timed out
if( IsEmptyReload( ))
{
if( SetAnimation( ACT_VM_PUMP_EMPTY ) == -1 )
SetAnimation( ACT_VM_PUMP );
}
else SetAnimation( ACT_VM_PUMP );
PostReload(); // post effects
m_iStepReload = NOT_IN_RELOAD;
}
}
else
{
WeaponIdle();
}
return;
}
}
// catch all
if( ShouldWeaponIdle( ))
{
WeaponIdle();
}
}
void CBasePlayerItem :: DestroyItem( void )
{
if( m_pPlayer )
{
// if attached to a player, remove.
m_pPlayer->RemovePlayerItem( this );
}
Kill( );
}
int CBasePlayerItem :: AddToPlayer( CBasePlayer *pPlayer )
{
m_pPlayer = pPlayer;
pPlayer->pev->weapons |= BIT( m_iId );
pev->sequence = 0; // to avoid some strange problems
if( !m_iPrimaryAmmoType )
m_iPrimaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo1() );
if( !m_iSecondaryAmmoType )
m_iSecondaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo2() );
if( FBitSet( iFlags(), ITEM_FLAG_AUTOFIRE ))
m_iWeaponAutoFire = TRUE; // enable auto-fire as default
MESSAGE_BEGIN( MSG_ONE, gmsgWeapPickup, NULL, pPlayer->pev );
WRITE_BYTE( m_iId );
MESSAGE_END();
return AddWeapon();
}
void CBasePlayerItem :: Drop( void )
{
// Wargon: Оружие неюзабельно.
SetUse( NULL );
m_iItemCaps = CBaseEntity :: ObjectCaps();
SetTouch( NULL );
SetThink( &CBasePlayerItem :: SUB_Remove );
SetNextThink( 0.1 );
}
void CBasePlayerItem :: Kill( void )
{
// Wargon: Оружие неюзабельно.
SetUse( NULL );
m_iItemCaps = CBaseEntity :: ObjectCaps();
SetTouch( NULL );
SetThink( &CBasePlayerItem :: SUB_Remove );
SetNextThink( 0.1 );
}
void CBasePlayerItem :: Deploy( void )
{
ApplyPlayerSettings( FALSE );
m_iStepReload = 0;
if( iMaxClip() > 0 && m_iClip <= 0 ) // weapon have clip and clip is empty ?
{
if( !DefaultDeploy( ACT_VM_DEPLOY_EMPTY )) // try to playing "deploy_empty" anim
DefaultDeploy( ACT_VM_DEPLOY ); // custom animation not found, play standard animation
}
else DefaultDeploy( ACT_VM_DEPLOY ); // just playing standard anim
}
void CBasePlayerItem :: Holster( bool force )
{
ApplyPlayerSettings( TRUE );
if( iMaxClip() > 0 && m_iClip <= 0 ) // weapon have clip and clip is empty ?
{
if( !DefaultHolster( ACT_VM_HOLSTER_EMPTY, force )) // try to playing "holster_empty" anim
DefaultHolster( ACT_VM_HOLSTER, force );
}
else DefaultHolster( ACT_VM_HOLSTER, force ); // just playing standard anim
}
void CBasePlayerItem :: WeaponToggleMode( void )
{
if( !FBitSet( iFlags(), ITEM_FLAG_ALLOW_FIREMODE ))
return;
if( m_iWeaponAutoFire )
{
UTIL_ShowMessage( "#WEAPON_SEMI_AUTO", m_pPlayer );
m_iWeaponAutoFire = 0;
}
else
{
UTIL_ShowMessage( "#WEAPON_FULL_AUTO", m_pPlayer );
m_iWeaponAutoFire = 1;
}
EMIT_SOUND( m_pPlayer->edict(), CHAN_WEAPON, "weapons/weapon_chengemode.wav", 0.8, ATTN_NORM );
// FIXME: play anim here
}
void CBasePlayerItem :: AttachToPlayer ( CBasePlayer *pPlayer )
{
pev->movetype = MOVETYPE_FOLLOW;
pev->aiment = pPlayer->edict();
pev->effects = EF_NODRAW;
pev->solid = SOLID_NOT;
pev->targetname = iStringNull;
pev->owner = pPlayer->edict();
SetNextThink( 0.1 );
SetTouch( NULL );
SetThink( NULL );
SetUse( NULL );
m_iItemCaps = CBaseEntity :: ObjectCaps();
}
int CBasePlayerItem :: UpdateClientData( CBasePlayer *pPlayer )
{
BOOL bSend = FALSE;
int state = 0;
// update weapon body
if( m_iClientBody != m_iBody )
{
pev->body = (pev->body % NUM_HANDS) + NUM_HANDS * m_iBody;
MESSAGE_BEGIN( MSG_ONE, gmsgWeaponBody, NULL, m_pPlayer->pev );
WRITE_BYTE( pev->body );
MESSAGE_END();
m_iClientBody = m_iBody;
}
// update weapon skin
if( m_iClientSkin != m_iSkin )
{
pev->skin = m_iSkin;
MESSAGE_BEGIN( MSG_ONE, gmsgWeaponSkin, NULL, m_pPlayer->pev );
WRITE_BYTE( pev->skin );
MESSAGE_END();
m_iClientSkin = m_iSkin;
}
// update weapon anim
if( pPlayer->pev->weaponanim != m_iClientAnim )
{
MESSAGE_BEGIN( MSG_ONE, gmsgWeaponAnim, NULL, m_pPlayer->pev );
WRITE_BYTE( pPlayer->pev->weaponanim ); // sequence number
WRITE_BYTE( pev->framerate * 8 ); // playing speed
MESSAGE_END();
m_iClientAnim = pPlayer->pev->weaponanim;
}
if( pPlayer->m_pActiveItem == this )
{
if( pPlayer->m_fOnTarget )
state = WEAPON_IS_ONTARGET;
else state = 1;
}
// forcing send of all data!
if( !pPlayer->m_fWeapon )
bSend = TRUE;
// This is the current or last weapon, so the state will need to be updated
if( this == pPlayer->m_pActiveItem || this == pPlayer->m_pClientActiveItem )
{
if( pPlayer->m_pActiveItem != pPlayer->m_pClientActiveItem )
{
bSend = TRUE;
}
}
// if the ammo, state, or fov has changed, update the weapon
if( m_iClip != m_iClientClip || state != m_iClientWeaponState || pPlayer->m_iFOV != pPlayer->m_iClientFOV )
{
bSend = TRUE;
}
if( bSend )
{
MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pPlayer->pev );
WRITE_BYTE( state );
WRITE_BYTE( m_iId );
WRITE_BYTE( m_iClip );
MESSAGE_END();
m_iClientClip = m_iClip;
m_iClientWeaponState = state;
pPlayer->m_fWeapon = TRUE;
}
if( m_pNext )
m_pNext->UpdateClientData( pPlayer );
return 1;
}
//=========================================================
// IsUseable - this function determines whether or not a
// weapon is useable by the player in its current state.
// (does it have ammo loaded? do I have any ammo for the
// weapon?, etc)
//=========================================================
BOOL CBasePlayerItem :: IsUseable( void )
{
if( m_iClip <= 0 && ( m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] <= 0 && iMaxAmmo1() != -1 ))
{
return FALSE;
}
return TRUE;
}
//=========================================================
//=========================================================
int CBasePlayerItem :: PrimaryAmmoIndex( void )
{
return m_iPrimaryAmmoType;
}
//=========================================================
//=========================================================
int CBasePlayerItem :: SecondaryAmmoIndex( void )
{
return -1;
}
//=========================================================
// RetireWeapon - no more ammo for this gun, put it away.
//=========================================================
void CBasePlayerItem :: RetireWeapon( void )
{
// first, no viewmodel at all.
m_pPlayer->pev->viewmodel = iStringNull;
m_pPlayer->pev->weaponmodel = iStringNull;
//m_pPlayer->pev->viewmodelindex = NULL;
g_pGameRules->GetNextBestWeapon( m_pPlayer, this );
}
//*********************************************************
// weaponbox code:
//*********************************************************
LINK_ENTITY_TO_CLASS( weaponbox, CWeaponBox );
TYPEDESCRIPTION CWeaponBox :: m_SaveData[] =
{
DEFINE_ARRAY( CWeaponBox, m_rgAmmo, FIELD_INTEGER, MAX_AMMO_SLOTS ),
DEFINE_ARRAY( CWeaponBox, m_rgiszAmmo, FIELD_STRING, MAX_AMMO_SLOTS ),
DEFINE_ARRAY( CWeaponBox, m_rgpPlayerItems, FIELD_CLASSPTR, MAX_ITEM_TYPES ),
DEFINE_FIELD( CWeaponBox, m_cAmmoTypes, FIELD_INTEGER ),
}; IMPLEMENT_SAVERESTORE( CWeaponBox, CBaseEntity );
//=========================================================
//
//=========================================================
void CWeaponBox :: Precache( void )
{
if( pev->model != iStringNull )
PRECACHE_MODEL( STRING( pev->model ));
}
//=========================================================
//=========================================================
void CWeaponBox :: KeyValue( KeyValueData *pkvd )
{
if( FStrEq( pkvd->szKeyName, "reflection" ))
{
switch(atoi(pkvd->szValue))
{
case 1: pev->effects |= EF_NOREFLECT; break;
case 2: pev->effects |= EF_REFLECTONLY; break;
}
}
else if ( m_cAmmoTypes < MAX_AMMO_SLOTS )
{
PackAmmo( ALLOC_STRING(pkvd->szKeyName), atoi(pkvd->szValue) );
m_cAmmoTypes++;// count this new ammo type.
pkvd->fHandled = TRUE;
}
else
{
ALERT( at_error, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS );
}
}
//=========================================================
// CWeaponBox - Spawn
//=========================================================
void CWeaponBox :: Spawn( void )
{
if( GET_SERVER_STATE() == SERVER_LOADING )
pev->model = MAKE_STRING( "models/w_weaponbox.mdl" );
Precache( );
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_TRIGGER;
UTIL_SetSize( pev, g_vecZero, g_vecZero );
if( pev->model != iStringNull )
SET_MODEL( ENT(pev), STRING( pev->model ));
}
//=========================================================
// CWeaponBox - Kill - the think function that removes the
// box from the world.
//=========================================================
void CWeaponBox :: Kill( void )
{
CBasePlayerItem *pWeapon;
int i;
// destroy the weapons
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
{
pWeapon = m_rgpPlayerItems[ i ];
while ( pWeapon )
{
pWeapon->SetThink(&CBasePlayerItem::SUB_Remove);
pWeapon->SetNextThink( 0.1 );
pWeapon = pWeapon->m_pNext;
}
}
// remove the box
UTIL_Remove( this );
}
//=========================================================
// CWeaponBox - Touch: try to add my contents to the toucher
// if the toucher is a player.
//=========================================================
void CWeaponBox :: Touch( CBaseEntity *pOther )
{
if ( !(pev->flags & FL_ONGROUND ) )
{
return;
}
if ( !pOther->IsPlayer() )
{
// only players may touch a weaponbox.
return;
}
if ( !pOther->IsAlive() )
{
// no dead guys.
return;
}
CBasePlayer *pPlayer = (CBasePlayer *)pOther;
int i;
// dole out ammo
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ )
{
if ( !FStringNull( m_rgiszAmmo[ i ] ) )
{
// there's some ammo of this type.
pPlayer->GiveAmmo( m_rgAmmo[ i ], STRING( m_rgiszAmmo[ i ] ));
// now empty the ammo from the weaponbox since we just gave it to the player
m_rgiszAmmo[ i ] = iStringNull;
m_rgAmmo[ i ] = 0;
}
}
// go through my weapons and try to give the usable ones to the player.
// it's important the the player be given ammo first, so the weapons code doesn't refuse
// to deploy a better weapon that the player may pick up because he has no ammo for it.
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
{
if ( m_rgpPlayerItems[ i ] )
{
CBasePlayerItem *pItem;
// have at least one weapon in this slot
while ( m_rgpPlayerItems[ i ] )
{
pItem = m_rgpPlayerItems[ i ];
m_rgpPlayerItems[ i ] = m_rgpPlayerItems[ i ]->m_pNext;// unlink this weapon from the box
if ( pPlayer->AddPlayerItem( pItem ) )
{
pItem->AttachToPlayer( pPlayer );
}
}
}
}
EMIT_SOUND( pOther->edict(), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM );
SetTouch(NULL);
UTIL_Remove(this);
}
//=========================================================
// CWeaponBox - PackWeapon: Add this weapon to the box
//=========================================================
BOOL CWeaponBox :: PackWeapon( CBasePlayerItem *pWeapon )
{
// is one of these weapons already packed in this box?
if ( HasWeapon( pWeapon ) )
{
return FALSE;// box can only hold one of each weapon type
}
if ( pWeapon->m_pPlayer )
{
if ( !pWeapon->m_pPlayer->RemovePlayerItem( pWeapon ) )
{
// failed to unhook the weapon from the player!
return FALSE;
}
}
int iWeaponSlot = pWeapon->iItemSlot();
if ( m_rgpPlayerItems[ iWeaponSlot ] )
{
// there's already one weapon in this slot, so link this into the slot's column
pWeapon->m_pNext = m_rgpPlayerItems[ iWeaponSlot ];
m_rgpPlayerItems[ iWeaponSlot ] = pWeapon;
}
else
{
// first weapon we have for this slot
m_rgpPlayerItems[ iWeaponSlot ] = pWeapon;
pWeapon->m_pNext = NULL;
}
pWeapon->pev->spawnflags |= SF_NORESPAWN;// never respawn
pWeapon->pev->movetype = MOVETYPE_NONE;
pWeapon->pev->solid = SOLID_NOT;
pWeapon->pev->effects |= EF_NODRAW;
pWeapon->pev->modelindex = 0;
pWeapon->pev->model = iStringNull;
pWeapon->pev->owner = edict();
pWeapon->SetThink( NULL );// crowbar may be trying to swing again, etc.
pWeapon->SetTouch( NULL );
pWeapon->m_pPlayer = NULL;
return TRUE;
}
//=========================================================
// MaxAmmoCarry - pass in a name and this function will tell
// you the maximum amount of that type of ammunition that a
// player can carry.
//=========================================================
int CWeaponBox :: MaxAmmoCarry( int iszName )
{
for ( int i = 0; i < MAX_WEAPONS; i++ )
{
if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo1 && !Q_strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo1 ) )
return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo1;
if ( CBasePlayerItem::ItemInfoArray[i].pszAmmo2 && !Q_strcmp( STRING(iszName), CBasePlayerItem::ItemInfoArray[i].pszAmmo2 ) )
return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo2;
}
ALERT( at_error, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) );
return -1;
}
//=========================================================
// CWeaponBox - PackAmmo
//=========================================================
BOOL CWeaponBox :: PackAmmo( int iszName, int iCount )
{
int iMaxCarry;
if ( FStringNull( iszName ) )
{
// error here
ALERT ( at_debug, "NULL String in PackAmmo!\n" );
return FALSE;
}
iMaxCarry = MaxAmmoCarry( iszName );
if ( iMaxCarry != -1 && iCount > 0 )
{
GiveAmmo( iCount, STRING( iszName ));
return TRUE;
}
return FALSE;
}
//=========================================================
// CWeaponBox - GiveAmmo
//=========================================================
int CWeaponBox :: GiveAmmo( int iCount, const char *szName )
{
int i;
for (i = 1; i < MAX_AMMO_SLOTS && !FStringNull( m_rgiszAmmo[i] ); i++)
{
if (!Q_stricmp( szName, STRING( m_rgiszAmmo[i] )))
{
AmmoInfo *pAmmo = UTIL_FindAmmoType( szName );
if( !pAmmo ) return -1;
int iAdd = Q_min( iCount, pAmmo->iMaxCarry - m_rgAmmo[i] );
if ( iCount == 0 || iAdd > 0 )
{
m_rgAmmo[i] += iAdd;
return i;
}
return -1;
}
}
if (i < MAX_AMMO_SLOTS)
{
m_rgiszAmmo[i] = MAKE_STRING( szName );
m_rgAmmo[i] = iCount;
return i;
}
ALERT( at_debug, "out of named ammo slots\n");
return i;
}
//=========================================================
// CWeaponBox::HasWeapon - is a weapon of this type already
// packed in this box?
//=========================================================
BOOL CWeaponBox :: HasWeapon( CBasePlayerItem *pCheckItem )
{
CBasePlayerItem *pItem = m_rgpPlayerItems[pCheckItem->iItemSlot()];
while (pItem)
{
if (FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname) ))
{
return TRUE;
}
pItem = pItem->m_pNext;
}
return FALSE;
}
//=========================================================
// CWeaponBox::IsEmpty - is there anything in this box?
//=========================================================
BOOL CWeaponBox :: IsEmpty( void )
{
int i;
for ( i = 0 ; i < MAX_ITEM_TYPES ; i++ )
{
if ( m_rgpPlayerItems[ i ] )
{
return FALSE;
}
}
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i++ )
{
if ( !FStringNull( m_rgiszAmmo[ i ] ) )
{
// still have a bit of this type of ammo
return FALSE;
}
}
return TRUE;
}
//=========================================================
//=========================================================
void CWeaponBox :: SetObjectCollisionBox( void )
{
if( !Q_stricmp( STRING( pev->model ), "models/w_weaponbox.mdl" ))
{
pev->absmin = pev->origin + Vector(-16, -16, 0);
pev->absmax = pev->origin + Vector(16, 16, 16);
}
else
{
pev->absmin = pev->origin + Vector(-24, -24, 0);
pev->absmax = pev->origin + Vector(24, 24, 16);
}
}
// buz: painkiller item
class CPainkiller : public CBasePlayerAmmo
{
void Spawn( void )
{
Precache( );
SET_MODEL(ENT(pev), "models/w_painkiller.mdl");
CBasePlayerAmmo::Spawn( );
}
void Precache( void )
{
PRECACHE_MODEL ("models/w_painkiller.mdl");
PRECACHE_SOUND("items/painkiller_pickup.wav");
}
BOOL AddAmmo( CBaseEntity *pOther )
{
int bResult = (pOther->GiveAmmo( 1, "painkillers" ) != -1 );
if (bResult)
{
EMIT_SOUND(ENT(pev), CHAN_ITEM, "items/painkiller_pickup.wav", 1, ATTN_NORM);
pOther->pev->weapons |= (1<<WEAPON_PAINKILLER);
// Wargon: Возможность таргетить энтити подбором паинкиллера.
SUB_UseTargets( pOther, USE_TOGGLE, 0 );
}
return bResult;
}
};
LINK_ENTITY_TO_CLASS( item_painkiller, CPainkiller );