hlsdk-xash3d/dlls/weapons.cpp

1664 lines
44 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"
extern CGraph WorldGraph;
extern int gEvilImpulse101;
#define NOT_USED 255
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
ItemInfo CBasePlayerItem::ItemInfoArray[MAX_WEAPONS];
AmmoInfo CBasePlayerItem::AmmoInfoArray[MAX_AMMO_SLOTS];
extern int gmsgCurWeapon;
MULTIDAMAGE gMultiDamage;
#define TRACER_FREQ 4 // Tracers fire every fourth bullet
//=========================================================
// 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 MaxAmmoCarry( int iszName )
{
for( int i = 0; i < MAX_WEAPONS; i++ )
{
if( CBasePlayerItem::ItemInfoArray[i].pszAmmo1 && !strcmp( STRING( iszName ), CBasePlayerItem::ItemInfoArray[i].pszAmmo1 ) )
return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo1;
if( CBasePlayerItem::ItemInfoArray[i].pszAmmo2 && !strcmp( STRING( iszName ), CBasePlayerItem::ItemInfoArray[i].pszAmmo2 ) )
return CBasePlayerItem::ItemInfoArray[i].iMaxAmmo2;
}
ALERT( at_console, "MaxAmmoCarry() doesn't recognize '%s'!\n", STRING( iszName ) );
return -1;
}
/*
==============================================================================
MULTI-DAMAGE
Collects multiple small damages into a single damage
==============================================================================
*/
//
// ClearMultiDamage - resets the global multi damage accumulator
//
void ClearMultiDamage( void )
{
gMultiDamage.pEntity = NULL;
gMultiDamage.amount = 0;
gMultiDamage.type = 0;
}
//
// ApplyMultiDamage - inflicts contents of global multi damage register on gMultiDamage.pEntity
//
// GLOBALS USED:
// gMultiDamage
void ApplyMultiDamage( entvars_t *pevInflictor, entvars_t *pevAttacker )
{
Vector vecSpot1;//where blood comes from
Vector vecDir;//direction blood should go
TraceResult tr;
if( !gMultiDamage.pEntity )
return;
gMultiDamage.pEntity->TakeDamage( pevInflictor, pevAttacker, gMultiDamage.amount, gMultiDamage.type );
}
// GLOBALS USED:
// gMultiDamage
void AddMultiDamage( entvars_t *pevInflictor, CBaseEntity *pEntity, float flDamage, int bitsDamageType )
{
if( !pEntity )
return;
gMultiDamage.type |= bitsDamageType;
if( pEntity != gMultiDamage.pEntity )
{
ApplyMultiDamage( pevInflictor,pevInflictor ); // UNDONE: wrong attacker!
gMultiDamage.pEntity = pEntity;
gMultiDamage.amount = 0;
}
gMultiDamage.amount += flDamage;
}
/*
================
SpawnBlood
================
*/
void SpawnBlood( Vector vecSpot, int bloodColor, float flDamage )
{
UTIL_BloodDrips( vecSpot, g_vecAttackDir, bloodColor, (int)flDamage );
}
int DamageDecal( CBaseEntity *pEntity, int bitsDamageType )
{
if( !pEntity )
return ( DECAL_GUNSHOT1 + RANDOM_LONG( 0, 4 ) );
return pEntity->DamageDecal( bitsDamageType );
}
void DecalGunshot( TraceResult *pTrace, int iBulletType )
{
// Is the entity valid
if( !UTIL_IsValidEntity( pTrace->pHit ) )
return;
if( VARS( pTrace->pHit )->solid == SOLID_BSP || VARS( pTrace->pHit )->movetype == MOVETYPE_PUSHSTEP )
{
CBaseEntity *pEntity = NULL;
// Decal the wall with a gunshot
if( !FNullEnt( pTrace->pHit ) )
pEntity = CBaseEntity::Instance( pTrace->pHit );
switch( iBulletType )
{
case BULLET_PLAYER_9MM:
case BULLET_MONSTER_9MM:
case BULLET_PLAYER_MP5:
case BULLET_MONSTER_MP5:
case BULLET_PLAYER_BUCKSHOT:
case BULLET_PLAYER_357:
default:
// smoke and decal
UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) );
break;
case BULLET_MONSTER_12MM:
// smoke and decal
UTIL_GunshotDecalTrace( pTrace, DamageDecal( pEntity, DMG_BULLET ) );
break;
case BULLET_PLAYER_CROWBAR:
// wall decal
UTIL_DecalTrace( pTrace, DamageDecal( pEntity, DMG_CLUB ) );
break;
}
}
}
//
// EjectBrass - tosses a brass shell from passed origin at passed velocity
//
void EjectBrass( const Vector &vecOrigin, const Vector &vecVelocity, float rotation, int model, int soundtype )
{
// FIX: when the player shoots, their gun isn't in the same position as it is on the model other players see.
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin );
WRITE_BYTE( TE_MODEL );
WRITE_COORD( vecOrigin.x );
WRITE_COORD( vecOrigin.y );
WRITE_COORD( vecOrigin.z );
WRITE_COORD( vecVelocity.x );
WRITE_COORD( vecVelocity.y );
WRITE_COORD( vecVelocity.z );
WRITE_ANGLE( rotation );
WRITE_SHORT( model );
WRITE_BYTE( soundtype );
WRITE_BYTE( 25 );// 2.5 seconds
MESSAGE_END();
}
#if 0
// UNDONE: This is no longer used?
void ExplodeModel( const Vector &vecOrigin, float speed, int model, int count )
{
MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin );
WRITE_BYTE( TE_EXPLODEMODEL );
WRITE_COORD( vecOrigin.x );
WRITE_COORD( vecOrigin.y );
WRITE_COORD( vecOrigin.z );
WRITE_COORD( speed );
WRITE_SHORT( model );
WRITE_SHORT( count );
WRITE_BYTE( 15 );// 1.5 seconds
MESSAGE_END();
}
#endif
int giAmmoIndex = 0;
// Precaches the ammo and queues the ammo info for sending to clients
void AddAmmoNameToAmmoRegistry( const char *szAmmoname )
{
// make sure it's not already in the registry
for( int i = 0; i < MAX_AMMO_SLOTS; i++ )
{
if( !CBasePlayerItem::AmmoInfoArray[i].pszName)
continue;
if( stricmp( CBasePlayerItem::AmmoInfoArray[i].pszName, szAmmoname ) == 0 )
return; // ammo already in registry, just quite
}
giAmmoIndex++;
ASSERT( giAmmoIndex < MAX_AMMO_SLOTS );
if( giAmmoIndex >= MAX_AMMO_SLOTS )
giAmmoIndex = 0;
CBasePlayerItem::AmmoInfoArray[giAmmoIndex].pszName = szAmmoname;
CBasePlayerItem::AmmoInfoArray[giAmmoIndex].iId = giAmmoIndex; // yes, this info is redundant
}
// Precaches the weapon and queues the weapon info for sending to clients
void UTIL_PrecacheOtherWeapon( const char *szClassname )
{
edict_t *pent;
pent = CREATE_NAMED_ENTITY( MAKE_STRING( szClassname ) );
if( FNullEnt( pent ) )
{
ALERT( at_console, "NULL Ent in UTIL_PrecacheOtherWeapon\n" );
return;
}
CBaseEntity *pEntity = CBaseEntity::Instance( VARS( pent ) );
if( pEntity )
{
ItemInfo II = {0};
pEntity->Precache();
if( ( (CBasePlayerItem*)pEntity )->GetItemInfo( &II ) )
{
CBasePlayerItem::ItemInfoArray[II.iId] = II;
if( II.pszAmmo1 && *II.pszAmmo1 )
{
AddAmmoNameToAmmoRegistry( II.pszAmmo1 );
}
if( II.pszAmmo2 && *II.pszAmmo2 )
{
AddAmmoNameToAmmoRegistry( II.pszAmmo2 );
}
}
}
REMOVE_ENTITY( pent );
}
// called by worldspawn
void W_Precache( void )
{
memset( CBasePlayerItem::ItemInfoArray, 0, sizeof(CBasePlayerItem::ItemInfoArray) );
memset( CBasePlayerItem::AmmoInfoArray, 0, sizeof(CBasePlayerItem::AmmoInfoArray) );
giAmmoIndex = 0;
// custom items...
// common world objects
UTIL_PrecacheOther( "item_suit" );
UTIL_PrecacheOther( "item_healthkit" );
UTIL_PrecacheOther( "item_battery" );
UTIL_PrecacheOther( "item_antidote" );
UTIL_PrecacheOther( "item_security" );
UTIL_PrecacheOther( "item_longjump" );
// shotgun
UTIL_PrecacheOtherWeapon( "weapon_shotgun" );
UTIL_PrecacheOther( "ammo_buckshot" );
// crowbar
UTIL_PrecacheOtherWeapon( "weapon_crowbar" );
// glock
UTIL_PrecacheOtherWeapon( "weapon_9mmhandgun" );
UTIL_PrecacheOther( "ammo_9mmclip" );
// mp5
UTIL_PrecacheOtherWeapon( "weapon_9mmAR" );
UTIL_PrecacheOther( "ammo_9mmAR" );
UTIL_PrecacheOther( "ammo_ARgrenades" );
// 9mm ammo box
UTIL_PrecacheOther( "ammo_9mmbox" );
#if !OEM_BUILD && !HLDEMO_BUILD
// python
UTIL_PrecacheOtherWeapon( "weapon_357" );
UTIL_PrecacheOther( "ammo_357" );
// gauss
UTIL_PrecacheOtherWeapon( "weapon_gauss" );
UTIL_PrecacheOther( "ammo_gaussclip" );
// rpg
UTIL_PrecacheOtherWeapon( "weapon_rpg" );
UTIL_PrecacheOther( "ammo_rpgclip" );
// crossbow
UTIL_PrecacheOtherWeapon( "weapon_crossbow" );
UTIL_PrecacheOther( "ammo_crossbow" );
// egon
UTIL_PrecacheOtherWeapon( "weapon_egon" );
#endif
// tripmine
UTIL_PrecacheOtherWeapon( "weapon_tripmine" );
#if !OEM_BUILD && !HLDEMO_BUILD
// satchel charge
UTIL_PrecacheOtherWeapon( "weapon_satchel" );
#endif
// hand grenade
UTIL_PrecacheOtherWeapon("weapon_handgrenade");
#if !OEM_BUILD && !HLDEMO_BUILD
// squeak grenade
UTIL_PrecacheOtherWeapon( "weapon_snark" );
// hornetgun
UTIL_PrecacheOtherWeapon( "weapon_hornetgun" );
if( g_pGameRules->IsDeathmatch() )
{
UTIL_PrecacheOther( "weaponbox" );// container for dropped deathmatch weapons
}
#endif
g_sModelIndexFireball = PRECACHE_MODEL( "sprites/zerogxplode.spr" );// fireball
g_sModelIndexWExplosion = PRECACHE_MODEL( "sprites/WXplo1.spr" );// underwater fireball
g_sModelIndexSmoke = PRECACHE_MODEL( "sprites/steam1.spr" );// smoke
g_sModelIndexBubbles = PRECACHE_MODEL( "sprites/bubble.spr" );//bubbles
g_sModelIndexBloodSpray = PRECACHE_MODEL( "sprites/bloodspray.spr" ); // initial blood
g_sModelIndexBloodDrop = PRECACHE_MODEL( "sprites/blood.spr" ); // splattered blood
g_sModelIndexLaser = PRECACHE_MODEL( g_pModelNameLaser );
g_sModelIndexLaserDot = PRECACHE_MODEL( "sprites/laserdot.spr" );
// used by explosions
PRECACHE_MODEL( "models/grenade.mdl" );
PRECACHE_MODEL( "sprites/explode1.spr" );
PRECACHE_SOUND( "weapons/debris1.wav" );// explosion aftermaths
PRECACHE_SOUND( "weapons/debris2.wav" );// explosion aftermaths
PRECACHE_SOUND( "weapons/debris3.wav" );// explosion aftermaths
PRECACHE_SOUND( "weapons/grenade_hit1.wav" );//grenade
PRECACHE_SOUND( "weapons/grenade_hit2.wav" );//grenade
PRECACHE_SOUND( "weapons/grenade_hit3.wav" );//grenade
PRECACHE_SOUND( "weapons/bullet_hit1.wav" ); // hit by bullet
PRECACHE_SOUND( "weapons/bullet_hit2.wav" ); // hit by bullet
PRECACHE_SOUND( "items/weapondrop1.wav" );// weapon falls to the ground
}
TYPEDESCRIPTION CBasePlayerItem::m_SaveData[] =
{
DEFINE_FIELD( CBasePlayerItem, m_pPlayer, FIELD_CLASSPTR ),
DEFINE_FIELD( CBasePlayerItem, m_pNext, FIELD_CLASSPTR ),
//DEFINE_FIELD( CBasePlayerItem, m_fKnown, FIELD_INTEGER ),Reset to zero on load
DEFINE_FIELD( CBasePlayerItem, m_iId, FIELD_INTEGER ),
// DEFINE_FIELD( CBasePlayerItem, m_iIdPrimary, FIELD_INTEGER ),
// DEFINE_FIELD( CBasePlayerItem, m_iIdSecondary, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CBasePlayerItem, CBaseAnimating )
TYPEDESCRIPTION CBasePlayerWeapon::m_SaveData[] =
{
#if CLIENT_WEAPONS
DEFINE_FIELD( CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_FLOAT ),
DEFINE_FIELD( CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_FLOAT ),
DEFINE_FIELD( CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_FLOAT ),
#else // CLIENT_WEAPONS
DEFINE_FIELD( CBasePlayerWeapon, m_flNextPrimaryAttack, FIELD_TIME ),
DEFINE_FIELD( CBasePlayerWeapon, m_flNextSecondaryAttack, FIELD_TIME ),
DEFINE_FIELD( CBasePlayerWeapon, m_flTimeWeaponIdle, FIELD_TIME ),
#endif // CLIENT_WEAPONS
DEFINE_FIELD( CBasePlayerWeapon, m_iPrimaryAmmoType, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerWeapon, m_iSecondaryAmmoType, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerWeapon, m_iClip, FIELD_INTEGER ),
DEFINE_FIELD( CBasePlayerWeapon, m_iDefaultAmmo, FIELD_INTEGER ),
//DEFINE_FIELD( CBasePlayerWeapon, m_iClientClip, FIELD_INTEGER ), reset to zero on load so hud gets updated correctly
//DEFINE_FIELD( CBasePlayerWeapon, m_iClientWeaponState, FIELD_INTEGER ), reset to zero on load so hud gets updated correctly
};
IMPLEMENT_SAVERESTORE( CBasePlayerWeapon, CBasePlayerItem )
void CBasePlayerItem::SetObjectCollisionBox( void )
{
pev->absmin = pev->origin + Vector( -24, -24, 0 );
pev->absmax = pev->origin + Vector( 24, 24, 16 );
}
//=========================================================
// Sets up movetype, size, solidtype for a new weapon.
//=========================================================
void CBasePlayerItem::FallInit( void )
{
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_BBOX;
UTIL_SetOrigin( pev, pev->origin );
UTIL_SetSize( pev, Vector( 0, 0, 0 ), Vector( 0, 0, 0 ) );//pointsize until it lands on the ground.
SetTouch( &CBasePlayerItem::DefaultTouch );
SetThink( &CBasePlayerItem::FallThink );
pev->nextthink = gpGlobals->time + 0.1f;
}
//=========================================================
// 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 )
{
pev->nextthink = gpGlobals->time + 0.1f;
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( ENT( pev ), CHAN_VOICE, "items/weapondrop1.wav", 1, ATTN_NORM, 0, pitch );
}
// lie flat
pev->angles.x = 0;
pev->angles.z = 0;
Materialize();
}
else if( m_pPlayer )
{
SetThink( NULL );
}
if( g_pGameRules->IsBustingGame())
{
if( !FNullEnt( pev->owner ))
return;
if( FClassnameIs( pev, "weapon_egon" ))
UTIL_Remove( this );
}
}
//=========================================================
// 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( ENT( pev ), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 );
pev->effects &= ~EF_NODRAW;
pev->effects |= EF_MUZZLEFLASH;
}
pev->solid = SOLID_TRIGGER;
UTIL_SetOrigin( pev, pev->origin );// link into world.
SetTouch( &CBasePlayerItem::DefaultTouch );
SetThink( NULL );
}
//=========================================================
// 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 )
{
Materialize();
return;
}
pev->nextthink = gpGlobals->time + time;
}
//=========================================================
// 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:
return;
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.
CBaseEntity *pNewWeapon = CBaseEntity::Create( STRING( pev->classname ), g_pGameRules->VecWeaponRespawnSpot( this ), pev->angles, pev->owner );
if( pNewWeapon )
{
pNewWeapon->pev->effects |= EF_NODRAW;// invisible for now
pNewWeapon->SetTouch( NULL );// no touch
pNewWeapon->SetThink( &CBasePlayerItem::AttemptToMaterialize );
DROP_TO_FLOOR( ENT( pev ) );
// 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->pev->nextthink = g_pGameRules->FlWeaponRespawnTime( this );
}
else
{
ALERT( at_console, "Respawn failed to create %s!\n", STRING( pev->classname ) );
}
return pNewWeapon;
}
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 ) )
{
AttachToPlayer( pPlayer );
EMIT_SOUND( ENT( pPlayer->pev ), CHAN_ITEM, "items/gunpickup2.wav", 1, ATTN_NORM );
}
SUB_UseTargets( pOther, USE_TOGGLE, 0 ); // UNDONE: when should this happen?
}
BOOL CanAttack( float attack_time, float curtime, BOOL isPredicted )
{
#if CLIENT_WEAPONS
if( !isPredicted )
#else
if( 1 )
#endif
{
return ( attack_time <= curtime ) ? TRUE : FALSE;
}
else
{
return ( (static_cast<int>(::floor(attack_time * 1000.0f)) * 1000.0f) <= 0.0f) ? TRUE : FALSE;
}
}
void CBasePlayerWeapon::ItemPostFrame( void )
{
if( ( m_fInReload ) && ( m_pPlayer->m_flNextAttack <= UTIL_WeaponTimeBase() ) )
{
// 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_pPlayer->TabulateAmmo();
m_fInReload = FALSE;
}
if( !(m_pPlayer->pev->button & IN_ATTACK ) )
{
m_flLastFireTime = 0.0f;
}
if( ( m_pPlayer->pev->button & IN_ATTACK2 ) && CanAttack( m_flNextSecondaryAttack, gpGlobals->time, UseDecrement() ) )
{
if( pszAmmo2() && !m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] )
{
m_fFireOnEmpty = TRUE;
}
m_pPlayer->TabulateAmmo();
SecondaryAttack();
m_pPlayer->pev->button &= ~IN_ATTACK2;
}
else if( ( m_pPlayer->pev->button & IN_ATTACK ) && CanAttack( m_flNextPrimaryAttack, gpGlobals->time, UseDecrement() ) )
{
if( ( m_iClip == 0 && pszAmmo1() ) || ( iMaxClip() == -1 && !m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] ) )
{
m_fFireOnEmpty = TRUE;
}
m_pPlayer->TabulateAmmo();
PrimaryAttack();
}
else if( 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( !( m_pPlayer->pev->button & ( IN_ATTACK | IN_ATTACK2 ) ) )
{
// no fire buttons down
m_fFireOnEmpty = FALSE;
if( !IsUseable() && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0f : gpGlobals->time ) )
{
// weapon isn't useable, switch.
if( !( iFlags() & ITEM_FLAG_NOAUTOSWITCHEMPTY ) && g_pGameRules->GetNextBestWeapon( m_pPlayer, this ) )
{
m_flNextPrimaryAttack = ( UseDecrement() ? 0.0f : gpGlobals->time ) + 0.3f;
return;
}
}
else
{
// weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
if( m_iClip == 0 && !(iFlags() & ITEM_FLAG_NOAUTORELOAD ) && m_flNextPrimaryAttack < ( UseDecrement() ? 0.0f : gpGlobals->time ) )
{
Reload();
return;
}
}
WeaponIdle();
return;
}
// catch all
if( ShouldWeaponIdle() )
{
WeaponIdle();
}
}
void CBasePlayerItem::DestroyItem( void )
{
if( m_pPlayer )
{
// if attached to a player, remove.
m_pPlayer->RemovePlayerItem( this, false );
}
Kill();
}
int CBasePlayerItem::AddToPlayer( CBasePlayer *pPlayer )
{
m_pPlayer = pPlayer;
return TRUE;
}
void CBasePlayerItem::Drop( void )
{
SetTouch( NULL );
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1f;
}
void CBasePlayerItem::Kill( void )
{
SetTouch( NULL );
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1f;
}
void CBasePlayerItem::Holster( int skiplocal /* = 0 */ )
{
m_pPlayer->pev->viewmodel = 0;
m_pPlayer->pev->weaponmodel = 0;
}
void CBasePlayerItem::AttachToPlayer( CBasePlayer *pPlayer )
{
pev->movetype = MOVETYPE_FOLLOW;
pev->solid = SOLID_NOT;
pev->aiment = pPlayer->edict();
pev->effects = EF_NODRAW; // ??
pev->modelindex = 0;// server won't send down to clients if modelindex == 0
pev->model = iStringNull;
pev->owner = pPlayer->edict();
pev->nextthink = 0;// Remove think - prevents futher attempts to materialize
SetTouch( NULL );
SetThink( NULL );
}
// CALLED THROUGH the newly-touched weapon's instance. The existing player weapon is pOriginal
int CBasePlayerWeapon::AddDuplicate( CBasePlayerItem *pOriginal )
{
if( m_iDefaultAmmo )
{
return ExtractAmmo( (CBasePlayerWeapon *)pOriginal );
}
else
{
// a dead player dropped this.
return ExtractClipAmmo( (CBasePlayerWeapon *)pOriginal );
}
}
int CBasePlayerWeapon::AddToPlayer( CBasePlayer *pPlayer )
{
int bResult = CBasePlayerItem::AddToPlayer( pPlayer );
pPlayer->pev->weapons |= ( 1 << m_iId );
if( !m_iPrimaryAmmoType )
{
m_iPrimaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo1() );
m_iSecondaryAmmoType = pPlayer->GetAmmoIndex( pszAmmo2() );
}
if( bResult )
return AddWeapon();
return FALSE;
}
int CBasePlayerWeapon::UpdateClientData( CBasePlayer *pPlayer )
{
BOOL bSend = FALSE;
int state = 0;
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;
}
void CBasePlayerWeapon::SendWeaponAnim( int iAnim, int skiplocal, int body )
{
if( UseDecrement() )
skiplocal = !pev->oldbuttons;
else
skiplocal = 0;
m_pPlayer->pev->weaponanim = iAnim;
#if CLIENT_WEAPONS
if( skiplocal && ENGINE_CANSKIP( m_pPlayer->edict() ) )
return;
#endif
MESSAGE_BEGIN( MSG_ONE, SVC_WEAPONANIM, NULL, m_pPlayer->pev );
WRITE_BYTE( iAnim ); // sequence number
WRITE_BYTE( pev->body ); // weaponmodel bodygroup.
MESSAGE_END();
}
BOOL CBasePlayerWeapon::AddPrimaryAmmo( int iCount, char *szName, int iMaxClip, int iMaxCarry )
{
int iIdAmmo;
if( iMaxClip < 1 )
{
m_iClip = -1;
iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry );
}
else if( m_iClip == 0 )
{
int i;
i = Q_min( m_iClip + iCount, iMaxClip ) - m_iClip;
m_iClip += i;
iIdAmmo = m_pPlayer->GiveAmmo( iCount - i, szName, iMaxCarry );
}
else
{
iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMaxCarry );
}
// m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] = iMaxCarry; // hack for testing
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( ENT( pev ), CHAN_ITEM, "items/9mmclip1.wav", 1, ATTN_NORM );
}
}
return iIdAmmo > 0 ? TRUE : FALSE;
}
BOOL CBasePlayerWeapon::AddSecondaryAmmo( int iCount, char *szName, int iMax )
{
int iIdAmmo;
iIdAmmo = m_pPlayer->GiveAmmo( iCount, szName, iMax );
//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;
}
//=========================================================
// 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 CBasePlayerWeapon::IsUseable( void )
{
if( m_iClip > 0 )
{
return TRUE;
}
// Player has unlimited ammo for this weapon or does not use magazines
if( iMaxAmmo1() == WEAPON_NOCLIP )
{
return TRUE;
}
if( m_pPlayer->m_rgAmmo[PrimaryAmmoIndex()] > 0 )
{
return TRUE;
}
if( pszAmmo2() )
{
// Player has unlimited ammo for this weapon or does not use magazines
if( iMaxAmmo2() == WEAPON_NOCLIP )
{
return TRUE;
}
if( m_pPlayer->m_rgAmmo[SecondaryAmmoIndex()] > 0 )
{
return TRUE;
}
}
// clip is empty (or nonexistant) and the player has no more ammo of this type.
return FALSE;
}
BOOL CBasePlayerWeapon::CanDeploy( void )
{
BOOL bHasAmmo = 0;
if( !pszAmmo1() )
{
// this weapon doesn't use ammo, can always deploy.
return TRUE;
}
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;
}
if( !bHasAmmo )
{
return FALSE;
}
return TRUE;
}
BOOL CBasePlayerWeapon::DefaultDeploy( const char *szViewModel, const char *szWeaponModel, int iAnim, const char *szAnimExt, int skiplocal /* = 0 */, int body )
{
if( !CanDeploy() )
return FALSE;
m_pPlayer->TabulateAmmo();
m_pPlayer->pev->viewmodel = MAKE_STRING( szViewModel );
m_pPlayer->pev->weaponmodel = MAKE_STRING( szWeaponModel );
strcpy( m_pPlayer->m_szAnimExtention, szAnimExt );
SendWeaponAnim( iAnim, skiplocal, body );
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + 0.5f;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 1.0f;
m_flLastFireTime = 0.0f;
return TRUE;
}
BOOL CBasePlayerWeapon::DefaultReload( int iClipSize, int iAnim, float fDelay, int body )
{
if( m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] <= 0 )
return FALSE;
int j = Q_min( iClipSize - m_iClip, m_pPlayer->m_rgAmmo[m_iPrimaryAmmoType] );
if( j == 0 )
return FALSE;
m_pPlayer->m_flNextAttack = UTIL_WeaponTimeBase() + fDelay;
//!!UNDONE -- reload sound goes here !!!
SendWeaponAnim( iAnim, UseDecrement() ? 1 : 0 );
m_fInReload = TRUE;
m_flTimeWeaponIdle = UTIL_WeaponTimeBase() + 3.0f;
return TRUE;
}
BOOL CBasePlayerWeapon::PlayEmptySound( void )
{
if( m_iPlayEmptySound )
{
EMIT_SOUND( ENT( m_pPlayer->pev ), CHAN_WEAPON, "weapons/357_cock1.wav", 0.8, ATTN_NORM );
m_iPlayEmptySound = 0;
return 0;
}
return 0;
}
void CBasePlayerWeapon::ResetEmptySound( void )
{
m_iPlayEmptySound = 1;
}
//=========================================================
//=========================================================
int CBasePlayerWeapon::PrimaryAmmoIndex( void )
{
return m_iPrimaryAmmoType;
}
//=========================================================
//=========================================================
int CBasePlayerWeapon::SecondaryAmmoIndex( void )
{
return -1;
}
void CBasePlayerWeapon::Holster( int skiplocal /* = 0 */ )
{
m_fInReload = FALSE; // cancel any reload in progress.
m_pPlayer->pev->viewmodel = 0;
m_pPlayer->pev->weaponmodel = 0;
}
void CBasePlayerAmmo::Spawn( void )
{
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_TRIGGER;
UTIL_SetSize( pev, Vector( -16, -16, 0 ), Vector( 16, 16, 16 ) );
UTIL_SetOrigin( pev, pev->origin );
SetTouch( &CBasePlayerAmmo::DefaultTouch );
}
CBaseEntity* CBasePlayerAmmo::Respawn( void )
{
pev->effects |= EF_NODRAW;
SetTouch( NULL );
UTIL_SetOrigin( pev, g_pGameRules->VecAmmoRespawnSpot( this ) );// move to wherever I'm supposed to repawn.
SetThink( &CBasePlayerAmmo::Materialize );
pev->nextthink = g_pGameRules->FlAmmoRespawnTime( this );
return this;
}
void CBasePlayerAmmo::Materialize( void )
{
if( pev->effects & EF_NODRAW )
{
// changing from invisible state to visible.
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, "items/suitchargeok1.wav", 1, ATTN_NORM, 0, 150 );
pev->effects &= ~EF_NODRAW;
pev->effects |= EF_MUZZLEFLASH;
}
SetTouch( &CBasePlayerAmmo::DefaultTouch );
SetThink( NULL );
}
void CBasePlayerAmmo::DefaultTouch( CBaseEntity *pOther )
{
if( !pOther->IsPlayer() || IsPlayerBusting( pOther ))
{
return;
}
if( AddAmmo( pOther ) )
{
if( g_pGameRules->AmmoShouldRespawn( this ) == GR_AMMO_RESPAWN_YES )
{
Respawn();
}
else
{
SetTouch( NULL );
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1f;
}
}
else if( gEvilImpulse101 )
{
// evil impulse 101 hack, kill always
SetTouch( NULL );
SetThink( &CBaseEntity::SUB_Remove );
pev->nextthink = gpGlobals->time + 0.1f;
}
}
//=========================================================
// 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 CBasePlayerWeapon::ExtractAmmo( CBasePlayerWeapon *pWeapon )
{
int iReturn = 0;
if( pszAmmo1() != NULL )
{
// blindly call with m_iDefaultAmmo. It's either going to be a value or zero. If it is zero,
// we only get the ammo in the weapon's clip, which is what we want.
iReturn |= pWeapon->AddPrimaryAmmo( m_iDefaultAmmo, (char *)pszAmmo1(), iMaxClip(), iMaxAmmo1() );
m_iDefaultAmmo = 0;
}
if( pszAmmo2() != NULL )
{
iReturn |= pWeapon->AddSecondaryAmmo( 0, (char *)pszAmmo2(), iMaxAmmo2() );
}
return iReturn;
}
//=========================================================
// called by the new item's class with the existing item as parameter
//=========================================================
int CBasePlayerWeapon::ExtractClipAmmo( CBasePlayerWeapon *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, (char *)pszAmmo1(), iMaxAmmo1() ); // , &m_iPrimaryAmmoType
}
//=========================================================
// RetireWeapon - no more ammo for this gun, put it away.
//=========================================================
void CBasePlayerWeapon::RetireWeapon( void )
{
// first, no viewmodel at all.
m_pPlayer->pev->viewmodel = iStringNull;
m_pPlayer->pev->weaponmodel = iStringNull;
//m_pPlayer->pev->viewmodelindex = NULL;
if( !g_pGameRules->GetNextBestWeapon( m_pPlayer, this ) )
{
// Another weapon wasn't selected. Get rid of current one
if( m_pPlayer->m_pActiveItem == this )
{
m_pPlayer->ResetAutoaim();
m_pPlayer->m_pActiveItem->Holster();
m_pPlayer->m_pLastItem = NULL;
m_pPlayer->m_pActiveItem = NULL;
}
}
}
//=========================================================================
// GetNextAttackDelay - An accurate way of calcualting the next attack time.
//=========================================================================
float CBasePlayerWeapon::GetNextAttackDelay( float delay )
{
if( m_flLastFireTime == 0 || m_flNextPrimaryAttack == -1.0f )
{
// At this point, we are assuming that the client has stopped firing
// and we are going to reset our book keeping variables.
m_flLastFireTime = gpGlobals->time;
m_flPrevPrimaryAttack = delay;
}
// calculate the time between this shot and the previous
float flTimeBetweenFires = gpGlobals->time - m_flLastFireTime;
float flCreep = 0.0f;
if( flTimeBetweenFires > 0 )
flCreep = flTimeBetweenFires - m_flPrevPrimaryAttack; // postive or negative
// save the last fire time
m_flLastFireTime = gpGlobals->time;
float flNextAttack = UTIL_WeaponTimeBase() + delay - flCreep;
// we need to remember what the m_flNextPrimaryAttack time is set to for each shot,
// store it as m_flPrevPrimaryAttack.
m_flPrevPrimaryAttack = flNextAttack - UTIL_WeaponTimeBase();
//char szMsg[256];
//_snprintf( szMsg, sizeof(szMsg), "next attack time: %0.4f\n", gpGlobals->time + flNextAttack );
//OutputDebugString( szMsg );
return flNextAttack;
}
//*********************************************************
// 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 )
{
PRECACHE_MODEL( "models/w_weaponbox.mdl" );
}
//=========================================================
//=========================================================
void CWeaponBox::KeyValue( KeyValueData *pkvd )
{
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_console, "WeaponBox too full! only %d ammotypes allowed\n", MAX_AMMO_SLOTS );
}
}
//=========================================================
// CWeaponBox - Spawn
//=========================================================
void CWeaponBox::Spawn( void )
{
Precache();
pev->movetype = MOVETYPE_TOSS;
pev->solid = SOLID_TRIGGER;
UTIL_SetSize( pev, g_vecZero, g_vecZero );
SET_MODEL( ENT( pev ), "models/w_weaponbox.mdl" );
}
//=========================================================
// 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( &CBaseEntity::SUB_Remove );
pWeapon->pev->nextthink = gpGlobals->time + 0.1f;
pWeapon = pWeapon->m_pNext;
}
}
// remove the box
UTIL_Remove( this );
}
static const char* IsAmmoForExhaustibleWeapon(const char* ammoName, int& weaponId)
{
for (int i=0; i<MAX_WEAPONS; ++i) {
ItemInfo& II = CBasePlayerItem::ItemInfoArray[i];
if ((II.iFlags & ITEM_FLAG_EXHAUSTIBLE) && II.pszAmmo1 && FStrEq(ammoName, II.pszAmmo1)) {
weaponId = II.iId;
return II.pszName;
}
}
return 0;
}
//=========================================================
// 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] ) )
{
// horrific HACK to give player an exhaustible weapon as a real weapon, not just ammo
int exhaustibleWeaponId;
const char* weaponName = IsAmmoForExhaustibleWeapon(STRING(m_rgiszAmmo[i]), exhaustibleWeaponId);
if (weaponName) {
bool foundWeapon = false;
for( int j = 0; j < MAX_ITEM_TYPES && !foundWeapon; j++ )
{
CBasePlayerItem *pPlayerItem = pPlayer->m_rgpPlayerItems[j];
while( pPlayerItem )
{
if (pPlayerItem->m_iId == exhaustibleWeaponId) {
foundWeapon = true;
break;
}
pPlayerItem = pPlayerItem->m_pNext;
}
}
if (!foundWeapon) {
CBasePlayerWeapon* weapon = (CBasePlayerWeapon*)Create(weaponName, pev->origin, pev->angles);
if (weapon) {
weapon->pev->spawnflags |= SF_NORESPAWN;
weapon->m_iDefaultAmmo = 0;
if (pPlayer->AddPlayerItem(weapon)) {
weapon->AttachToPlayer(pPlayer);
}
}
}
}
// there's some ammo of this type.
pPlayer->GiveAmmo( m_rgAmmo[i], STRING( m_rgiszAmmo[i] ), MaxAmmoCarry( m_rgiszAmmo[i] ) );
//ALERT( at_console, "Gave %d rounds of %s\n", 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] )
{
//ALERT( at_console, "trying to give %s\n", STRING( m_rgpPlayerItems[i]->pev->classname ) );
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, true ) )
{
// 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;
//ALERT( at_console, "packed %s\n", STRING( pWeapon->pev->classname ) );
return TRUE;
}
//=========================================================
// CWeaponBox - PackAmmo
//=========================================================
BOOL CWeaponBox::PackAmmo( int iszName, int iCount )
{
int iMaxCarry;
if( FStringNull( iszName ) )
{
// error here
ALERT( at_console, "NULL String in PackAmmo!\n" );
return FALSE;
}
iMaxCarry = MaxAmmoCarry( iszName );
if( iMaxCarry != -1 && iCount > 0 )
{
//ALERT( at_console, "Packed %d rounds of %s\n", iCount, STRING( iszName ) );
GiveAmmo( iCount, STRING( iszName ), iMaxCarry );
return TRUE;
}
return FALSE;
}
//=========================================================
// CWeaponBox - GiveAmmo
//=========================================================
int CWeaponBox::GiveAmmo( int iCount, const char *szName, int iMax, int *pIndex/* = NULL*/ )
{
int i;
for( i = 1; i < MAX_AMMO_SLOTS && !FStringNull( m_rgiszAmmo[i] ); i++ )
{
if( stricmp( szName, STRING( m_rgiszAmmo[i] ) ) == 0 )
{
if( pIndex )
*pIndex = i;
int iAdd = Q_min( iCount, iMax - m_rgAmmo[i] );
if( iCount == 0 || iAdd > 0 )
{
m_rgAmmo[i] += iAdd;
return i;
}
return -1;
}
}
if( i < MAX_AMMO_SLOTS )
{
if( pIndex )
*pIndex = i;
m_rgiszAmmo[i] = MAKE_STRING( szName );
m_rgAmmo[i] = iCount;
return i;
}
ALERT( at_console, "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 )
{
pev->absmin = pev->origin + Vector( -16, -16, 0 );
pev->absmax = pev->origin + Vector( 16, 16, 16 );
}
void CBasePlayerWeapon::PrintState( void )
{
ALERT( at_console, "primary: %f\n", (double)m_flNextPrimaryAttack );
ALERT( at_console, "idle : %f\n", (double)m_flTimeWeaponIdle );
//ALERT( at_console, "nextrl : %f\n", m_flNextReload );
//ALERT( at_console, "nextpum: %f\n", m_flPumpTime );
//ALERT( at_console, "m_frt : %f\n", m_fReloadTime );
ALERT( at_console, "m_finre: %i\n", m_fInReload );
//ALERT( at_console, "m_finsr: %i\n", m_fInSpecialReload );
ALERT( at_console, "m_iclip: %i\n", m_iClip );
}
TYPEDESCRIPTION CRpg::m_SaveData[] =
{
DEFINE_FIELD( CRpg, m_fSpotActive, FIELD_INTEGER ),
DEFINE_FIELD( CRpg, m_cActiveRockets, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CRpg, CBasePlayerWeapon )
TYPEDESCRIPTION CRpgRocket::m_SaveData[] =
{
DEFINE_FIELD( CRpgRocket, m_flIgniteTime, FIELD_TIME ),
DEFINE_FIELD( CRpgRocket, m_hLauncher, FIELD_EHANDLE ),
};
IMPLEMENT_SAVERESTORE( CRpgRocket, CGrenade )
TYPEDESCRIPTION CShotgun::m_SaveData[] =
{
DEFINE_FIELD( CShotgun, m_flNextReload, FIELD_TIME ),
DEFINE_FIELD( CShotgun, m_fInSpecialReload, FIELD_INTEGER ),
// DEFINE_FIELD( CShotgun, m_iShell, FIELD_INTEGER ),
DEFINE_FIELD( CShotgun, m_flPumpTime, FIELD_TIME ),
};
IMPLEMENT_SAVERESTORE( CShotgun, CBasePlayerWeapon )
TYPEDESCRIPTION CGauss::m_SaveData[] =
{
DEFINE_FIELD( CGauss, m_fInAttack, FIELD_INTEGER ),
//DEFINE_FIELD( CGauss, m_flStartCharge, FIELD_TIME ),
//DEFINE_FIELD( CGauss, m_flPlayAftershock, FIELD_TIME ),
//DEFINE_FIELD( CGauss, m_flNextAmmoBurn, FIELD_TIME ),
DEFINE_FIELD( CGauss, m_fPrimaryFire, FIELD_BOOLEAN ),
};
IMPLEMENT_SAVERESTORE( CGauss, CBasePlayerWeapon )
TYPEDESCRIPTION CEgon::m_SaveData[] =
{
//DEFINE_FIELD( CEgon, m_pBeam, FIELD_CLASSPTR ),
//DEFINE_FIELD( CEgon, m_pNoise, FIELD_CLASSPTR ),
//DEFINE_FIELD( CEgon, m_pSprite, FIELD_CLASSPTR ),
DEFINE_FIELD( CEgon, m_shootTime, FIELD_TIME ),
DEFINE_FIELD( CEgon, m_fireState, FIELD_INTEGER ),
DEFINE_FIELD( CEgon, m_fireMode, FIELD_INTEGER ),
DEFINE_FIELD( CEgon, m_shakeTime, FIELD_TIME ),
DEFINE_FIELD( CEgon, m_flAmmoUseTime, FIELD_TIME ),
};
IMPLEMENT_SAVERESTORE( CEgon, CBasePlayerWeapon )
TYPEDESCRIPTION CHgun::m_SaveData[] =
{
DEFINE_FIELD( CHgun, m_flRechargeTime, FIELD_TIME ),
DEFINE_FIELD( CHgun, m_iFirePhase, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CHgun, CBasePlayerWeapon )
TYPEDESCRIPTION CSatchel::m_SaveData[] =
{
DEFINE_FIELD( CSatchel, m_chargeReady, FIELD_INTEGER ),
};
IMPLEMENT_SAVERESTORE( CSatchel, CBasePlayerWeapon )