2014-01-14 19:43:04 +00:00
//
// Copyright (c) 1999, 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.
//
//
//===== player.cpp ========================================================
//
// functions dealing with the player
//
//
// $Workfile: player.cpp $
// $Date: 2002/11/22 21:12:55 $
//
//-------------------------------------------------------------------------------
// $Log: player.cpp,v $
// Revision 1.39 2002/11/22 21:12:55 Flayra
// - Added DMG_IGNOREARMOR flag for players (used for ending game quickly)
// - NULL checks for when owner of turret is removed after owner leaves team
// - Send deploy weapon switch to player, when "lastinv" is executed
//
// Revision 1.38 2002/11/15 19:09:40 Flayra
// - Reworked network metering to be easily toggleable
//
// Revision 1.37 2002/11/12 02:18:41 Flayra
// - Beginning of HLTV changes
//
// Revision 1.36 2002/10/24 21:18:23 Flayra
// - Major networking optimizations, to prevent overlflows after 20+ players on game reset
//
// Revision 1.35 2002/10/18 22:15:13 Flayra
// - Fixed problem where level5 gets stuck with redemption
//
// Revision 1.34 2002/10/16 00:40:39 Flayra
// - Fixed bug where player couldn't switch to next player while observing
// - Propagate health as short for larger aliens
// - Propagate auth mask
//
// Revision 1.33 2002/10/03 18:26:03 Flayra
// - Removed HL "flatline" sound on death
//
// Revision 1.32 2002/09/23 22:03:50 Flayra
// - Fixed problem where anims weren't playing properly in ready room
// - Fixed problem where gestation model anims were playing before model was updated
//
// Revision 1.31 2002/08/31 18:01:18 Flayra
// - Work at VALVe
//
// Revision 1.30 2002/08/09 00:17:03 Flayra
// - Allow moveleft and moveright to change targets, allow players to have only one primary weapon
//
// Revision 1.29 2002/07/23 16:48:21 Flayra
// - Refactored duplicate spawn code
//
// Revision 1.28 2002/07/10 14:36:09 Flayra
// - Fixed bug that caused "Battery" message to be sent every tick (this never showed up in HL because they never had a float armor value?)
//
// Revision 1.27 2002/07/08 16:31:39 Flayra
// - Level 1 doesn't make falling splat sound, dead players shouldn't block spawn points, replaced impulse hard-codes with constants
//
//===============================================================================
# include <cmath>
# include "extdll.h"
# include "util.h"
# include "cbase.h"
# include "player.h"
# include "trains.h"
# include "nodes.h"
# include "weapons.h"
# include "soundent.h"
# include "monsters.h"
# include "engine/shake.h"
# include "decals.h"
# include "gamerules.h"
# include "mod/AvHEntities.h"
# include "mod/AvHPlayer.h"
# include "mod/AvHGamerules.h"
# include "mod/AvHPlayerUpgrade.h"
# include "mod/AvHServerUtil.h"
# include "mod/AvHSharedUtil.h"
# include "mod/AvHServerVariables.h"
# include "mod/AvHHulls.h"
# include "mod/AvHMovementUtil.h"
# include "game.h"
# include "common/hltv.h"
# include "mod/AvHNetworkMessages.h"
# include "util/MathUtil.h"
// #define DUCKFIX
// Allow assignment within conditional
# pragma warning (disable: 4706)
extern DLL_GLOBAL ULONG g_ulModelIndexPlayer ;
extern DLL_GLOBAL BOOL g_fGameOver ;
extern DLL_GLOBAL BOOL g_fDrawLines ;
int gEvilImpulse101 ;
extern DLL_GLOBAL int g_iSkillLevel , gDisplayTitle ;
BOOL gInitHUD = TRUE ;
extern void CopyToBodyQue ( entvars_t * pev ) ;
extern void respawn ( entvars_t * pev , BOOL fCopyCorpse ) ;
extern Vector VecBModelOrigin ( entvars_t * pevBModel ) ;
//extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer );
// the world node graph
extern CGraph WorldGraph ;
# define TRAIN_ACTIVE 0x80
# define TRAIN_NEW 0xc0
# define TRAIN_OFF 0x00
# define TRAIN_NEUTRAL 0x01
# define TRAIN_SLOW 0x02
# define TRAIN_MEDIUM 0x03
# define TRAIN_FAST 0x04
# define TRAIN_BACK 0x05
# define FLASH_DRAIN_TIME 1.2 //100 units/3 minutes
# define FLASH_CHARGE_TIME 0.2 // 100 units/20 seconds (seconds per unit)
// Global Savedata for player
TYPEDESCRIPTION CBasePlayer : : m_playerSaveData [ ] =
{
DEFINE_FIELD ( CBasePlayer , m_afButtonLast , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_afButtonPressed , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_afButtonReleased , FIELD_INTEGER ) ,
DEFINE_ARRAY ( CBasePlayer , m_rgItems , FIELD_INTEGER , MAX_ITEMS ) ,
DEFINE_FIELD ( CBasePlayer , m_afPhysicsFlags , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_flTimeStepSound , FIELD_TIME ) ,
DEFINE_FIELD ( CBasePlayer , m_flTimeWeaponIdle , FIELD_TIME ) ,
DEFINE_FIELD ( CBasePlayer , m_flSwimTime , FIELD_TIME ) ,
DEFINE_FIELD ( CBasePlayer , m_flDuckTime , FIELD_TIME ) ,
DEFINE_FIELD ( CBasePlayer , m_flWallJumpTime , FIELD_TIME ) ,
DEFINE_FIELD ( CBasePlayer , m_flSuitUpdate , FIELD_TIME ) ,
DEFINE_ARRAY ( CBasePlayer , m_rgSuitPlayList , FIELD_INTEGER , CSUITPLAYLIST ) ,
DEFINE_FIELD ( CBasePlayer , m_iSuitPlayNext , FIELD_INTEGER ) ,
DEFINE_ARRAY ( CBasePlayer , m_rgiSuitNoRepeat , FIELD_INTEGER , CSUITNOREPEAT ) ,
DEFINE_ARRAY ( CBasePlayer , m_rgflSuitNoRepeatTime , FIELD_TIME , CSUITNOREPEAT ) ,
DEFINE_FIELD ( CBasePlayer , m_lastDamageAmount , FIELD_INTEGER ) ,
DEFINE_ARRAY ( CBasePlayer , m_rgpPlayerItems , FIELD_CLASSPTR , MAX_ITEM_TYPES ) ,
DEFINE_FIELD ( CBasePlayer , m_pActiveItem , FIELD_CLASSPTR ) ,
DEFINE_FIELD ( CBasePlayer , m_pLastItem , FIELD_CLASSPTR ) ,
DEFINE_ARRAY ( CBasePlayer , m_rgAmmo , FIELD_INTEGER , MAX_AMMO_SLOTS ) ,
DEFINE_FIELD ( CBasePlayer , m_idrowndmg , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_idrownrestored , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_tSneaking , FIELD_TIME ) ,
DEFINE_FIELD ( CBasePlayer , m_iTrain , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_bitsHUDDamage , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_flFallVelocity , FIELD_FLOAT ) ,
DEFINE_FIELD ( CBasePlayer , m_iTargetVolume , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_iWeaponVolume , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_iExtraSoundTypes , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_iWeaponFlash , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_fLongJump , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( CBasePlayer , m_fInitHUD , FIELD_BOOLEAN ) ,
DEFINE_FIELD ( CBasePlayer , m_tbdPrev , FIELD_TIME ) ,
DEFINE_FIELD ( CBasePlayer , m_pTank , FIELD_EHANDLE ) ,
DEFINE_FIELD ( CBasePlayer , m_iHideHUD , FIELD_INTEGER ) ,
DEFINE_FIELD ( CBasePlayer , m_iFOV , FIELD_INTEGER ) ,
} ;
void CBasePlayer : : Pain ( void )
{
float flRndSound ; //sound randomizer
flRndSound = RANDOM_FLOAT ( 0 , 1 ) ;
if ( flRndSound < = 0.33 )
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pl_pain5.wav " , 1 , ATTN_NORM ) ;
else if ( flRndSound < = 0.66 )
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pl_pain6.wav " , 1 , ATTN_NORM ) ;
else
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pl_pain7.wav " , 1 , ATTN_NORM ) ;
}
/*
*
*/
Vector VecVelocityForDamage ( float flDamage )
{
Vector vec ( RANDOM_FLOAT ( - 100 , 100 ) , RANDOM_FLOAT ( - 100 , 100 ) , RANDOM_FLOAT ( 200 , 300 ) ) ;
if ( flDamage > - 50 )
vec = vec * 0.7 ;
else if ( flDamage > - 200 )
vec = vec * 2 ;
else
vec = vec * 10 ;
return vec ;
}
#if 0 /*
static void ThrowGib ( entvars_t * pev , char * szGibModel , float flDamage )
{
edict_t * pentNew = CREATE_ENTITY ( ) ;
entvars_t * pevNew = VARS ( pentNew ) ;
pevNew - > origin = pev - > origin ;
SET_MODEL ( ENT ( pevNew ) , szGibModel ) ;
UTIL_SetSize ( pevNew , g_vecZero , g_vecZero ) ;
pevNew - > velocity = VecVelocityForDamage ( flDamage ) ;
pevNew - > movetype = MOVETYPE_BOUNCE ;
pevNew - > solid = SOLID_NOT ;
pevNew - > avelocity . x = RANDOM_FLOAT ( 0 , 600 ) ;
pevNew - > avelocity . y = RANDOM_FLOAT ( 0 , 600 ) ;
pevNew - > avelocity . z = RANDOM_FLOAT ( 0 , 600 ) ;
CHANGE_METHOD ( ENT ( pevNew ) , em_think , SUB_Remove ) ;
pevNew - > ltime = gpGlobals - > time ;
pevNew - > nextthink = gpGlobals - > time + RANDOM_FLOAT ( 10 , 20 ) ;
pevNew - > frame = 0 ;
pevNew - > flags = 0 ;
}
static void ThrowHead ( entvars_t * pev , char * szGibModel , floatflDamage )
{
SET_MODEL ( ENT ( pev ) , szGibModel ) ;
pev - > frame = 0 ;
pev - > nextthink = - 1 ;
pev - > movetype = MOVETYPE_BOUNCE ;
pev - > takedamage = DAMAGE_NO ;
pev - > solid = SOLID_NOT ;
pev - > view_ofs = Vector ( 0 , 0 , 8 ) ;
UTIL_SetSize ( pev , Vector ( - 16 , - 16 , 0 ) , Vector ( 16 , 16 , 56 ) ) ;
pev - > velocity = VecVelocityForDamage ( flDamage ) ;
pev - > avelocity = RANDOM_FLOAT ( - 1 , 1 ) * Vector ( 0 , 600 , 0 ) ;
pev - > origin . z - = 24 ;
ClearBits ( pev - > flags , FL_ONGROUND ) ;
}
*/
# endif
int TrainSpeed ( int iSpeed , int iMax )
{
float fSpeed , fMax ;
int iRet = 0 ;
fMax = ( float ) iMax ;
fSpeed = iSpeed ;
fSpeed = fSpeed / fMax ;
if ( iSpeed < 0 )
iRet = TRAIN_BACK ;
else if ( iSpeed = = 0 )
iRet = TRAIN_NEUTRAL ;
else if ( fSpeed < 0.33 )
iRet = TRAIN_SLOW ;
else if ( fSpeed < 0.66 )
iRet = TRAIN_MEDIUM ;
else
iRet = TRAIN_FAST ;
return iRet ;
}
void CBasePlayer : : DeathSound ( void )
{
// water death sounds
/*
if ( pev - > waterlevel = = 3 )
{
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/h2odeath.wav " , 1 , ATTN_NONE ) ;
return ;
}
*/
// temporarily using pain sounds for death sounds
switch ( RANDOM_LONG ( 1 , 5 ) )
{
case 1 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pl_pain5.wav " , 1 , ATTN_NORM ) ;
break ;
case 2 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pl_pain6.wav " , 1 , ATTN_NORM ) ;
break ;
case 3 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pl_pain7.wav " , 1 , ATTN_NORM ) ;
break ;
}
// play one of the suit death alarms
//EMIT_GROUPNAME_SUIT(ENT(pev), "HEV_DEAD");
}
// override takehealth
// bitsDamageType indicates type of damage healed.
int CBasePlayer : : TakeHealth ( float flHealth , int bitsDamageType )
{
return CBaseMonster : : TakeHealth ( flHealth , bitsDamageType ) ;
}
Vector CBasePlayer : : GetGunPosition ( )
{
// UTIL_MakeVectors(pev->v_angle);
// m_HackedGunPos = pev->view_ofs;
Vector origin ;
origin = pev - > origin + pev - > view_ofs ;
return origin ;
}
//=========================================================
// TraceAttack
//=========================================================
void CBasePlayer : : TraceAttack ( entvars_t * pevAttacker , float flDamage , Vector vecDir , TraceResult * ptr , int bitsDamageType )
{
if ( pev - > takedamage )
{
m_LastHitGroup = ptr - > iHitgroup ;
switch ( ptr - > iHitgroup )
{
case HITGROUP_GENERIC :
break ;
case HITGROUP_HEAD :
flDamage * = gSkillData . plrHead ;
break ;
case HITGROUP_CHEST :
flDamage * = gSkillData . plrChest ;
break ;
case HITGROUP_STOMACH :
flDamage * = gSkillData . plrStomach ;
break ;
case HITGROUP_LEFTARM :
case HITGROUP_RIGHTARM :
flDamage * = gSkillData . plrArm ;
break ;
case HITGROUP_LEFTLEG :
case HITGROUP_RIGHTLEG :
flDamage * = gSkillData . plrLeg ;
break ;
default :
break ;
}
SpawnBlood ( ptr - > vecEndPos , BloodColor ( ) , flDamage ) ; // a little surface blood.
TraceBleed ( flDamage , vecDir , ptr , bitsDamageType ) ;
AddMultiDamage ( pevAttacker , this , flDamage , bitsDamageType ) ;
}
}
void CBasePlayer : : SpawnClientSideCorpse ( )
{
// char *infobuffer = g_engfuncs.pfnGetInfoKeyBuffer( edict() );
// char *pModel = g_engfuncs.pfnInfoKeyValue( infobuffer, "model" );
//
// MESSAGE_BEGIN( MSG_ALL, gmsgSendCorpse );
// WRITE_STRING( pModel );
// WRITE_LONG ( pev->origin.x * 128.0);
// WRITE_LONG ( pev->origin.y * 128.0);
// WRITE_LONG ( pev->origin.z * 128.0);
// WRITE_COORD ( pev->angles.x );
// WRITE_COORD ( pev->angles.y );
// WRITE_COORD ( pev->angles.z );
// WRITE_LONG ( (pev->animtime - gpGlobals->time) * 100.0f );
// WRITE_BYTE ( pev->sequence );
// WRITE_BYTE ( pev->body );
// MESSAGE_END();
}
/*
Take some damage .
NOTE : each call to TakeDamage with bitsDamageType set to a time - based damage
type will cause the damage time countdown to be reset . Thus the ongoing effects of poison , radiation
etc are implemented with subsequent calls to TakeDamage using DMG_GENERIC .
*/
int CBasePlayer : : TakeDamage ( entvars_t * pevInflictor , entvars_t * pevAttacker , float flDamage , int bitsDamageType )
{
// have suit diagnose the problem - ie: report damage type
int bitsDamage = bitsDamageType ;
int ffound = TRUE ;
int fmajor ;
int fcritical ;
int fTookDamage ;
int ftrivial ;
//float flRatio;
//float flBonus;
float flHealthPrev = pev - > health ;
//flBonus = AvHPlayerUpgrade::GetArmorBonus(this->pev->iuser4);
//flRatio = AvHPlayerUpgrade::GetArmorRatio(this->pev->iuser4);
// if ( ( bitsDamageType & DMG_BLAST ) && g_pGameRules->IsMultiplayer() )
// {
// // blasts damage armor more.
// flBonus *= 2;
// }
// Already dead
if ( ! IsAlive ( ) )
return 0 ;
// go take the damage first
CBaseEntity * pAttacker = NULL ;
if ( pevAttacker )
{
pAttacker = CBaseEntity : : Instance ( pevAttacker ) ;
}
if ( ! g_pGameRules - > FPlayerCanTakeDamage ( this , pAttacker ) )
{
// Refuse the damage
return 0 ;
}
// keep track of amount of damage last sustained
m_lastDamageAmount = flDamage ;
if ( ! ( bitsDamageType & DMG_IGNOREARMOR ) )
{
flDamage = AvHPlayerUpgrade : : CalculateDamageLessArmor ( ( AvHUser3 ) this - > pev - > iuser3 , this - > pev - > iuser4 , flDamage , this - > pev - > armorvalue , bitsDamageType , GetGameRules ( ) - > GetNumActiveHives ( ( AvHTeamNumber ) this - > pev - > team ) ) ;
}
else
{
int a = 0 ;
}
// Armor.
// if (pev->armorvalue && !(bitsDamageType & (DMG_FALL | DMG_DROWN)) )// armor doesn't protect against fall or drown damage!
// {
// float flNew = flDamage * flRatio;
//
// float flArmor;
//
// flArmor = (flDamage - flNew) * flBonus;
//
// // Does this use more armor than we have?
// if (flArmor > pev->armorvalue)
// {
// flArmor = pev->armorvalue;
// flArmor *= (1/flBonus);
// flNew = flDamage - flArmor;
// pev->armorvalue = 0;
// }
// else
// pev->armorvalue -= flArmor;
//
// flDamage = flNew;
// }
fTookDamage = CBaseMonster : : TakeDamage ( pevInflictor , pevAttacker , flDamage , bitsDamageType ) ;
// reset damage time countdown for each type of time based damage player just sustained
{
for ( int i = 0 ; i < CDMG_TIMEBASED ; i + + )
if ( bitsDamageType & ( DMG_PARALYZE < < i ) )
m_rgbTimeBasedDamage [ i ] = 0 ;
}
// tell director about it
MESSAGE_BEGIN ( MSG_SPEC , SVC_DIRECTOR ) ;
WRITE_BYTE ( DIRECTOR_MESSAGE_SIZE ) ;
WRITE_BYTE ( DRC_CMD_EVENT ) ; // take damage event
WRITE_SHORT ( ENTINDEX ( this - > edict ( ) ) ) ; // index number of primary entity
WRITE_SHORT ( ENTINDEX ( ENT ( pevInflictor ) ) ) ; // index number of secondary entity
WRITE_LONG ( 5 ) ; // eventflags (priority and flags)
MESSAGE_END ( ) ;
// how bad is it, doc?
ftrivial = ( pev - > health > 75 | | m_lastDamageAmount < 5 ) ;
fmajor = ( m_lastDamageAmount > 25 ) ;
fcritical = ( pev - > health < 30 ) ;
// handle all bits set in this damage message,
// let the suit give player the diagnosis
// UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash )
// UNDONE: still need to record damage and heal messages for the following types
// DMG_BURN
// DMG_FREEZE
// DMG_BLAST
// DMG_SHOCK
m_bitsDamageType | = bitsDamage ; // Save this so we can report it to the client
m_bitsHUDDamage = - 1 ; // make sure the damage bits get resent
while ( fTookDamage & & ( ! ftrivial | | ( bitsDamage & DMG_TIMEBASED ) ) & & ffound & & bitsDamage )
{
ffound = FALSE ;
if ( bitsDamage & DMG_CLUB )
{
if ( fmajor )
SetSuitUpdate ( " !HEV_DMG4 " , FALSE , SUIT_NEXT_IN_30SEC ) ; // minor fracture
bitsDamage & = ~ DMG_CLUB ;
ffound = TRUE ;
}
if ( bitsDamage & ( DMG_FALL | DMG_CRUSH ) )
{
if ( fmajor )
SetSuitUpdate ( " !HEV_DMG5 " , FALSE , SUIT_NEXT_IN_30SEC ) ; // major fracture
else
SetSuitUpdate ( " !HEV_DMG4 " , FALSE , SUIT_NEXT_IN_30SEC ) ; // minor fracture
bitsDamage & = ~ ( DMG_FALL | DMG_CRUSH ) ;
ffound = TRUE ;
}
if ( bitsDamage & DMG_BULLET )
{
if ( m_lastDamageAmount > 5 )
SetSuitUpdate ( " !HEV_DMG6 " , FALSE , SUIT_NEXT_IN_30SEC ) ; // blood loss detected
//else
// SetSuitUpdate("!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC); // minor laceration
bitsDamage & = ~ DMG_BULLET ;
ffound = TRUE ;
}
if ( bitsDamage & DMG_SLASH )
{
if ( fmajor )
SetSuitUpdate ( " !HEV_DMG1 " , FALSE , SUIT_NEXT_IN_30SEC ) ; // major laceration
else
SetSuitUpdate ( " !HEV_DMG0 " , FALSE , SUIT_NEXT_IN_30SEC ) ; // minor laceration
bitsDamage & = ~ DMG_SLASH ;
ffound = TRUE ;
}
if ( bitsDamage & DMG_SONIC )
{
if ( fmajor )
SetSuitUpdate ( " !HEV_DMG2 " , FALSE , SUIT_NEXT_IN_1MIN ) ; // internal bleeding
bitsDamage & = ~ DMG_SONIC ;
ffound = TRUE ;
}
if ( bitsDamage & ( DMG_POISON | DMG_PARALYZE ) )
{
SetSuitUpdate ( " !HEV_DMG3 " , FALSE , SUIT_NEXT_IN_1MIN ) ; // blood toxins detected
bitsDamage & = ~ ( DMG_POISON | DMG_PARALYZE ) ;
ffound = TRUE ;
}
if ( bitsDamage & DMG_ACID )
{
SetSuitUpdate ( " !HEV_DET1 " , FALSE , SUIT_NEXT_IN_1MIN ) ; // hazardous chemicals detected
bitsDamage & = ~ DMG_ACID ;
ffound = TRUE ;
}
if ( bitsDamage & DMG_NERVEGAS )
{
SetSuitUpdate ( " !HEV_DET0 " , FALSE , SUIT_NEXT_IN_1MIN ) ; // biohazard detected
bitsDamage & = ~ DMG_NERVEGAS ;
ffound = TRUE ;
}
if ( bitsDamage & DMG_RADIATION )
{
SetSuitUpdate ( " !HEV_DET2 " , FALSE , SUIT_NEXT_IN_1MIN ) ; // radiation detected
bitsDamage & = ~ DMG_RADIATION ;
ffound = TRUE ;
}
if ( bitsDamage & DMG_SHOCK )
{
bitsDamage & = ~ DMG_SHOCK ;
ffound = TRUE ;
}
}
pev - > punchangle . x = - 2 ;
if ( fTookDamage & & ! ftrivial & & fmajor & & flHealthPrev > = 75 )
{
// first time we take major damage...
// turn automedic on if not on
SetSuitUpdate ( " !HEV_MED1 " , FALSE , SUIT_NEXT_IN_30MIN ) ; // automedic on
// give morphine shot if not given recently
SetSuitUpdate ( " !HEV_HEAL7 " , FALSE , SUIT_NEXT_IN_30MIN ) ; // morphine shot
}
if ( fTookDamage & & ! ftrivial & & fcritical & & flHealthPrev < 75 )
{
// already took major damage, now it's critical...
if ( pev - > health < 6 )
SetSuitUpdate ( " !HEV_HLTH3 " , FALSE , SUIT_NEXT_IN_10MIN ) ; // near death
else if ( pev - > health < 20 )
SetSuitUpdate ( " !HEV_HLTH2 " , FALSE , SUIT_NEXT_IN_10MIN ) ; // health critical
// give critical health warnings
if ( ! RANDOM_LONG ( 0 , 3 ) & & flHealthPrev < 50 )
SetSuitUpdate ( " !HEV_DMG7 " , FALSE , SUIT_NEXT_IN_5MIN ) ; //seek medical attention
}
// if we're taking time based damage, warn about its continuing effects
if ( fTookDamage & & ( bitsDamageType & DMG_TIMEBASED ) & & flHealthPrev < 75 )
{
if ( flHealthPrev < 50 )
{
if ( ! RANDOM_LONG ( 0 , 3 ) )
SetSuitUpdate ( " !HEV_DMG7 " , FALSE , SUIT_NEXT_IN_5MIN ) ; //seek medical attention
}
else
SetSuitUpdate ( " !HEV_HLTH1 " , FALSE , SUIT_NEXT_IN_10MIN ) ; // health dropping
}
//return fTookDamage;
int theReturnValue = 0 ;
if ( fTookDamage )
{
theReturnValue = flDamage ;
}
return theReturnValue ;
}
//=========================================================
// PackDeadPlayerItems - call this when a player dies to
// pack up the appropriate weapons and ammo items, and to
// destroy anything that shouldn't be packed.
//
// This is pretty brute force :(
//=========================================================
void CBasePlayer : : PackDeadPlayerItems ( void )
{
int iWeaponRules ;
int iAmmoRules ;
int i ;
CBasePlayerWeapon * rgpPackWeapons [ 20 ] ; // 20 hardcoded for now. How to determine exactly how many weapons we have?
int iPackAmmo [ MAX_AMMO_SLOTS + 1 ] ;
int iPW = 0 ; // index into packweapons array
int iPA = 0 ; // index into packammo array
memset ( rgpPackWeapons , NULL , sizeof ( rgpPackWeapons ) ) ;
memset ( iPackAmmo , - 1 , sizeof ( iPackAmmo ) ) ;
// get the game rules
iWeaponRules = g_pGameRules - > DeadPlayerWeapons ( this ) ;
iAmmoRules = g_pGameRules - > DeadPlayerAmmo ( this ) ;
if ( iWeaponRules = = GR_PLR_DROP_GUN_NO & & iAmmoRules = = GR_PLR_DROP_AMMO_NO )
{
// nothing to pack. Remove the weapons and return. Don't call create on the box!
RemoveAllItems ( TRUE ) ;
return ;
}
// go through all of the weapons and make a list of the ones to pack
for ( i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
if ( m_rgpPlayerItems [ i ] )
{
// there's a weapon here. Should I pack it?
CBasePlayerItem * pPlayerItem = m_rgpPlayerItems [ i ] ;
while ( pPlayerItem )
{
switch ( iWeaponRules )
{
case GR_PLR_DROP_GUN_ACTIVE :
if ( m_pActiveItem & & pPlayerItem = = m_pActiveItem )
{
// this is the active item. Pack it.
rgpPackWeapons [ iPW + + ] = ( CBasePlayerWeapon * ) pPlayerItem ;
}
break ;
case GR_PLR_DROP_GUN_ALL :
rgpPackWeapons [ iPW + + ] = ( CBasePlayerWeapon * ) pPlayerItem ;
break ;
default :
break ;
}
pPlayerItem = pPlayerItem - > m_pNext ;
}
}
}
// now go through ammo and make a list of which types to pack.
if ( iAmmoRules ! = GR_PLR_DROP_AMMO_NO )
{
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i + + )
{
if ( m_rgAmmo [ i ] > 0 )
{
// player has some ammo of this type.
switch ( iAmmoRules )
{
case GR_PLR_DROP_AMMO_ALL :
iPackAmmo [ iPA + + ] = i ;
break ;
case GR_PLR_DROP_AMMO_ACTIVE :
if ( m_pActiveItem & & i = = m_pActiveItem - > PrimaryAmmoIndex ( ) )
{
// this is the primary ammo type for the active weapon
iPackAmmo [ iPA + + ] = i ;
}
else if ( m_pActiveItem & & i = = m_pActiveItem - > SecondaryAmmoIndex ( ) )
{
// this is the secondary ammo type for the active weapon
iPackAmmo [ iPA + + ] = i ;
}
break ;
default :
break ;
}
}
}
}
// create a box to pack the stuff into.
CWeaponBox * pWeaponBox = ( CWeaponBox * ) CBaseEntity : : Create ( " weaponbox " , pev - > origin , pev - > angles , edict ( ) ) ;
pWeaponBox - > pev - > angles . x = 0 ; // don't let weaponbox tilt.
pWeaponBox - > pev - > angles . z = 0 ;
pWeaponBox - > SetThink ( & CWeaponBox : : Kill ) ;
pWeaponBox - > pev - > nextthink = gpGlobals - > time + 120 ;
// back these two lists up to their first elements
iPA = 0 ;
iPW = 0 ;
// pack the ammo
while ( iPackAmmo [ iPA ] ! = - 1 )
{
// Only pack items with ammo
const char * theAmmoName = CBasePlayerItem : : AmmoInfoArray [ iPackAmmo [ iPA ] ] . pszName ;
if ( theAmmoName & & theAmmoName ! = " " )
pWeaponBox - > PackAmmo ( MAKE_STRING ( theAmmoName ) , m_rgAmmo [ iPackAmmo [ iPA ] ] ) ;
iPA + + ;
}
// now pack all of the items in the lists
while ( rgpPackWeapons [ iPW ] )
{
// weapon unhooked from the player. Pack it into der box.
pWeaponBox - > PackWeapon ( rgpPackWeapons [ iPW ] ) ;
iPW + + ;
}
pWeaponBox - > pev - > velocity = pev - > velocity * 1.2 ; // weaponbox has player's velocity, then some.
RemoveAllItems ( TRUE ) ; // now strip off everything that wasn't handled by the code above.
}
void CBasePlayer : : DestroyAllItems ( BOOL removeSuit )
{
if ( m_pActiveItem )
{
ResetAutoaim ( ) ;
m_pActiveItem - > Holster ( ) ;
m_pActiveItem = NULL ;
}
m_pLastItem = NULL ;
int i ;
CBasePlayerItem * pPendingItem ;
for ( i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
m_pActiveItem = m_rgpPlayerItems [ i ] ;
while ( m_pActiveItem )
{
pPendingItem = m_pActiveItem - > m_pNext ;
m_pActiveItem - > DestroyItem ( ) ;
m_pActiveItem = pPendingItem ;
}
m_rgpPlayerItems [ i ] = NULL ;
}
m_pActiveItem = NULL ;
pev - > viewmodel = 0 ;
pev - > weaponmodel = 0 ;
if ( removeSuit )
pev - > weapons = 0 ;
else
pev - > weapons & = ~ WEAPON_ALLWEAPONS ;
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i + + )
m_rgAmmo [ i ] = 0 ;
// send Selected Weapon Message to our client
NetMsg_CurWeapon ( pev , 0 , 0 , 0 ) ;
}
void CBasePlayer : : RemoveAllItems ( BOOL removeSuit )
{
if ( m_pActiveItem )
{
ResetAutoaim ( ) ;
m_pActiveItem - > Holster ( ) ;
m_pActiveItem = NULL ;
}
m_pLastItem = NULL ;
int i ;
CBasePlayerItem * pPendingItem ;
for ( i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
m_pActiveItem = m_rgpPlayerItems [ i ] ;
while ( m_pActiveItem )
{
pPendingItem = m_pActiveItem - > m_pNext ;
m_pActiveItem - > Drop ( ) ;
m_pActiveItem = pPendingItem ;
}
m_rgpPlayerItems [ i ] = NULL ;
}
m_pActiveItem = NULL ;
pev - > viewmodel = 0 ;
pev - > weaponmodel = 0 ;
if ( removeSuit )
pev - > weapons = 0 ;
else
pev - > weapons & = ~ WEAPON_ALLWEAPONS ;
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i + + )
m_rgAmmo [ i ] = 0 ;
// UpdateClientData();
// // send Selected Weapon Message to our client
// MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev );
// WRITE_BYTE(0);
// WRITE_BYTE(0);
// WRITE_BYTE(0);
// MESSAGE_END();
}
/*
* GLOBALS ASSUMED SET : g_ulModelIndexPlayer
*
* ENTITY_METHOD ( PlayerDie )
*/
entvars_t * g_pevLastInflictor ; // Set in combat.cpp. Used to pass the damage inflictor for death messages.
// Better solution: Add as parameter to all Killed() functions.
void CBasePlayer : : Killed ( entvars_t * pevAttacker , int iGib )
{
CSound * pSound ;
// Holster weapon immediately, to allow it to cleanup
if ( m_pActiveItem )
m_pActiveItem - > Holster ( ) ;
GetGameRules ( ) - > PlayerKilled ( this , pevAttacker , g_pevLastInflictor ) ;
if ( m_pTank ! = NULL )
{
m_pTank - > Use ( this , this , USE_OFF , 0 ) ;
m_pTank = NULL ;
}
// this client isn't going to be thinking for a while, so reset the sound until they respawn
pSound = CSoundEnt : : SoundPointerForIndex ( CSoundEnt : : ClientSoundIndex ( edict ( ) ) ) ;
{
if ( pSound )
{
pSound - > Reset ( ) ;
}
}
SetAnimation ( PLAYER_DIE ) ;
m_iRespawnFrames = 0 ;
//pev->modelindex = g_ulModelIndexPlayer; // don't use eyes
// Clear buttons held down on death, fixes bug where player can't switch observer targets
m_afButtonLast = 0 ;
pev - > deadflag = DEAD_DYING ;
pev - > movetype = MOVETYPE_TOSS ;
ClearBits ( pev - > flags , FL_ONGROUND ) ;
if ( pev - > velocity . z < 10 )
pev - > velocity . z + = RANDOM_FLOAT ( 0 , 300 ) ;
// clear out the suit message cache so we don't keep chattering
SetSuitUpdate ( NULL , FALSE , 0 ) ;
// send "health" update message to zero
m_iClientHealth = 0 ;
NetMsg_Health ( pev , m_iClientHealth ) ;
// Tell Ammo Hud that the player is dead
NetMsg_CurWeapon ( pev , 0 , 0xFF , 0xFF ) ;
// reset FOV
pev - > fov = m_iFOV = m_iClientFOV = 0 ;
NetMsg_SetFOV ( pev , 0 ) ;
// UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12
// UTIL_ScreenFade( edict(), Vector(128,0,0), 6, 15, 255, FFADE_OUT | FFADE_MODULATE );
if ( ( pev - > health < - 40 & & iGib ! = GIB_NEVER ) | | iGib = = GIB_ALWAYS )
{
pev - > solid = SOLID_NOT ;
GibMonster ( ) ; // This clears pev->model
pev - > effects | = EF_NODRAW ;
return ;
}
DeathSound ( ) ;
pev - > angles . x = 0 ;
pev - > angles . z = 0 ;
SetThink ( & CBasePlayer : : PlayerDeathThink ) ;
pev - > nextthink = gpGlobals - > time + 0.1 ;
}
void CBasePlayer : : Suicide ( void )
{
AvHPlayer * thePlayer = dynamic_cast < AvHPlayer * > ( this ) ;
ASSERT ( thePlayer ) ;
if ( thePlayer & & thePlayer - > GetUsedKilled ( ) ) //: prevent exploitation of "kill" command.
return ;
// have the player kill themself
float theKillDelay = ns_cvar_float ( & avh_killdelay ) ;
# ifdef DEBUG
# ifndef AVH_EXTERNAL_BUILD
theKillDelay = 0 ;
# endif
# endif
if ( theKillDelay > 0.0f )
{
char theMessage [ 256 ] ;
sprintf ( theMessage , " Suiciding in %.1f seconds... \n " , theKillDelay ) ;
UTIL_SayText ( theMessage , this , ENTINDEX ( this - > edict ( ) ) ) ;
}
thePlayer - > SetUsedKilled ( true ) ;
SetThink ( & CBasePlayer : : SuicideThink ) ;
this - > pev - > nextthink = gpGlobals - > time + theKillDelay ;
}
void CBasePlayer : : SuicideThink ( void )
{
AvHPlayer * thePlayer = dynamic_cast < AvHPlayer * > ( this ) ;
if ( thePlayer & & thePlayer - > GetCanBeAffectedByEnemies ( ) )
{
this - > pev - > health = 0 ;
this - > Killed ( this - > pev , GIB_NEVER ) ;
}
}
// Set the activity based on an event or current state
void CBasePlayer : : SetAnimation ( PLAYER_ANIM playerAnim )
{
int animDesired ;
int gaitDesired ;
float speed ;
char szAnim [ 64 ] ;
bool theFoundAnim = true ;
int theDebugAnimations = BALANCE_VAR ( kDebugAnimations ) ;
bool reloadAnim = false ;
// Make sure the model is set, as gestating models aren't set before the animation starts playing
AvHPlayer * theAvHPlayer = dynamic_cast < AvHPlayer * > ( this ) ;
ASSERT ( theAvHPlayer ) ;
theAvHPlayer - > SetModelFromState ( ) ;
speed = pev - > velocity . Length2D ( ) ;
if ( pev - > flags & FL_FROZEN )
{
speed = 0 ;
playerAnim = PLAYER_IDLE ;
}
switch ( playerAnim )
{
case PLAYER_JUMP :
m_IdealActivity = ACT_HOP ;
break ;
case PLAYER_SUPERJUMP :
m_IdealActivity = ACT_LEAP ;
break ;
case PLAYER_DIE :
m_IdealActivity = ACT_DIESIMPLE ;
m_IdealActivity = GetDeathActivity ( ) ;
//this->GetAnimationForActivity(this->m_IdealActivity, szAnim);
//animDesired = LookupSequence( szAnim );
//if (animDesired == -1)
//{
// animDesired = 0;
//}
//this->pev->sequence = animDesired;
break ;
case PLAYER_ATTACK1 :
switch ( m_Activity )
{
case ACT_HOVER :
case ACT_SWIM :
case ACT_HOP :
case ACT_LEAP :
case ACT_DIESIMPLE :
m_IdealActivity = m_Activity ;
break ;
default :
m_IdealActivity = ACT_RANGE_ATTACK1 ;
break ;
}
break ;
case PLAYER_PRIME :
switch ( m_Activity )
{
case ACT_HOVER :
case ACT_SWIM :
case ACT_HOP :
case ACT_LEAP :
case ACT_DIESIMPLE :
m_IdealActivity = m_Activity ;
break ;
default :
m_IdealActivity = ACT_RANGE_PRIME ;
break ;
}
break ;
case PLAYER_RELOAD :
switch ( m_Activity )
{
case ACT_HOVER :
case ACT_SWIM :
case ACT_HOP :
case ACT_LEAP :
case ACT_DIESIMPLE :
m_IdealActivity = m_Activity ;
break ;
default :
m_IdealActivity = ACT_RELOAD ;
break ;
}
break ;
case PLAYER_RELOAD_START :
switch ( m_Activity )
{
case ACT_HOVER :
case ACT_SWIM :
case ACT_HOP :
case ACT_LEAP :
case ACT_DIESIMPLE :
m_IdealActivity = m_Activity ;
break ;
default :
m_IdealActivity = ACT_RELOAD_START ;
break ;
}
break ;
case PLAYER_RELOAD_INSERT :
switch ( m_Activity )
{
case ACT_HOVER :
case ACT_SWIM :
case ACT_HOP :
case ACT_LEAP :
case ACT_DIESIMPLE :
m_IdealActivity = m_Activity ;
break ;
default :
m_IdealActivity = ACT_RELOAD_INSERT ;
break ;
}
break ;
case PLAYER_RELOAD_END :
switch ( m_Activity )
{
case ACT_HOVER :
case ACT_SWIM :
case ACT_HOP :
case ACT_LEAP :
case ACT_DIESIMPLE :
m_IdealActivity = m_Activity ;
break ;
default :
m_IdealActivity = ACT_RELOAD_END ;
break ;
}
break ;
case PLAYER_IDLE :
case PLAYER_WALK :
if ( ! FBitSet ( pev - > flags , FL_ONGROUND ) & & ( m_Activity = = ACT_HOP | | m_Activity = = ACT_LEAP ) ) // Still jumping
{
m_IdealActivity = m_Activity ;
}
else if ( pev - > waterlevel > 1 )
{
if ( speed = = 0 )
m_IdealActivity = ACT_HOVER ;
else
m_IdealActivity = ACT_SWIM ;
}
else
{
if ( ( playerAnim = = PLAYER_IDLE ) & & ! strcmp ( this - > m_szAnimExtention , " " ) & & ( theAvHPlayer - > GetPlayMode ( ) ! = PLAYMODE_READYROOM ) )
{
m_IdealActivity = ACT_IDLE ;
}
else
{
m_IdealActivity = ACT_WALK ;
}
}
break ;
}
switch ( m_IdealActivity )
{
case ACT_DIESIMPLE :
case ACT_DIEBACKWARD :
case ACT_DIEFORWARD :
case ACT_DIEVIOLENT :
default :
if ( m_Activity = = m_IdealActivity )
return ;
m_Activity = m_IdealActivity ;
//animDesired = LookupActivity( m_Activity );
this - > GetAnimationForActivity ( this - > m_Activity , szAnim ) ;
animDesired = LookupSequence ( szAnim ) ;
// Already using the desired animation?
if ( pev - > sequence = = animDesired )
return ;
# ifdef DEBUG
if ( theDebugAnimations )
{
char theMessage [ 256 ] ;
sprintf ( theMessage , " Setting full-body animation (%s): %d (no gaitsequence) \n " , szAnim , animDesired ) ;
ALERT ( at_console , theMessage ) ;
}
# endif
pev - > gaitsequence = 0 ;
pev - > sequence = animDesired ;
pev - > frame = 0 ;
ResetSequenceInfo ( ) ;
return ;
// create seperate case for these so that they don't interfere with a reload - Elven
case ACT_HOVER :
case ACT_LEAP :
case ACT_SWIM :
case ACT_HOP :
reloadAnim = ( m_Activity = = ACT_RELOAD ) | | ( m_Activity = = ACT_RELOAD_START ) | | ( m_Activity = = ACT_RELOAD_INSERT ) | | ( m_Activity = = ACT_RELOAD_END ) ;
if ( m_Activity = = m_IdealActivity | | reloadAnim )
return ;
m_Activity = m_IdealActivity ;
//animDesired = LookupActivity( m_Activity );
this - > GetAnimationForActivity ( this - > m_Activity , szAnim ) ;
animDesired = LookupSequence ( szAnim ) ;
// Already using the desired animation?
if ( pev - > sequence = = animDesired )
return ;
pev - > gaitsequence = 0 ;
pev - > sequence = animDesired ;
pev - > frame = 0 ;
ResetSequenceInfo ( ) ;
return ;
case ACT_RANGE_ATTACK1 :
case ACT_RANGE_PRIME :
this - > GetAnimationForActivity ( this - > m_IdealActivity , szAnim ) ;
animDesired = LookupSequence ( szAnim ) ;
if ( animDesired = = - 1 )
{
animDesired = 0 ;
theFoundAnim = false ;
}
if ( pev - > sequence ! = animDesired | | ! m_fSequenceLoops )
{
pev - > frame = 0 ;
}
if ( ! m_fSequenceLoops )
{
pev - > effects | = EF_NOINTERP ;
}
m_Activity = m_IdealActivity ;
if ( theFoundAnim )
{
# ifdef DEBUG
if ( theDebugAnimations )
{
char theMessage [ 256 ] ;
sprintf ( theMessage , " %s%s \n " , " Setting attack animation: " , szAnim ) ;
ALERT ( at_console , theMessage ) ;
}
# endif
}
pev - > sequence = animDesired ;
ResetSequenceInfo ( ) ;
break ;
case ACT_RELOAD :
case ACT_RELOAD_START :
case ACT_RELOAD_INSERT :
case ACT_RELOAD_END :
this - > GetAnimationForActivity ( this - > m_IdealActivity , szAnim ) ;
animDesired = LookupSequence ( szAnim ) ;
if ( pev - > sequence ! = animDesired | | ! m_fSequenceLoops )
{
pev - > frame = 0 ;
}
m_Activity = m_IdealActivity ;
break ;
case ACT_WALK :
reloadAnim = ( m_Activity = = ACT_RELOAD ) | | ( m_Activity = = ACT_RELOAD_START ) | |
( m_Activity = = ACT_RELOAD_INSERT ) | | ( m_Activity = = ACT_RELOAD_END ) ;
if ( ( m_Activity ! = ACT_RANGE_ATTACK1 & & m_Activity ! = ACT_RANGE_PRIME & & ! reloadAnim /*m_Activity != ACT_RELOAD*/ ) | | m_fSequenceFinished )
{
this - > GetAnimationForActivity ( this - > m_IdealActivity , szAnim ) ;
animDesired = LookupSequence ( szAnim ) ;
if ( animDesired = = - 1 )
{
animDesired = 0 ;
}
m_Activity = ACT_WALK ;
}
else
{
animDesired = pev - > sequence ;
}
break ;
}
// Now handle gaitsequence
if ( FBitSet ( this - > pev - > flags , FL_DUCKING ) )
{
if ( speed = = 0 )
{
this - > GetAnimationForActivity ( ACT_CROUCHIDLE , szAnim , true ) ;
}
else
{
this - > GetAnimationForActivity ( ACT_CROUCH , szAnim , true ) ;
}
}
else if ( speed > this - > GetMaxWalkSpeed ( ) )
{
this - > GetAnimationForActivity ( ACT_RUN , szAnim , true ) ;
}
else if ( speed > 0 )
{
this - > GetAnimationForActivity ( ACT_WALK , szAnim , true ) ;
}
else
{
this - > GetAnimationForActivity ( ACT_IDLE , szAnim , true ) ;
}
// Set the gaitsequence
gaitDesired = LookupSequence ( szAnim , 1 ) ;
if ( gaitDesired = = - 1 )
{
gaitDesired = 0 ;
}
# ifdef DEBUG
if ( theDebugAnimations )
{
if ( ( this - > pev - > gaitsequence ! = gaitDesired ) | | ( this - > pev - > sequence ! = animDesired ) )
{
ALERT ( at_console , " Anim desired (%s): %d, gaitsequence: %d gaitdesired: %d \n " , szAnim , animDesired , pev - > gaitsequence , gaitDesired ) ;
}
}
# endif
this - > pev - > gaitsequence = gaitDesired ;
AvHPlayer * thePlayer = dynamic_cast < AvHPlayer * > ( this ) ;
if ( thePlayer & & ( thePlayer - > GetPlayMode ( ) = = PLAYMODE_READYROOM ) )
{
// Play some animation on top and bottom when not holding any weapons
animDesired = gaitDesired ;
}
// Already using the desired animation?
if ( pev - > sequence = = animDesired )
return ;
// Reset to first frame of desired animation
pev - > sequence = animDesired ;
pev - > frame = 0 ;
ResetSequenceInfo ( ) ;
}
/*
= = = = = = = = = = =
TabulateAmmo
This function is used to find and store
all the ammo we have into the ammo vars .
= = = = = = = = = = = =
*/
void CBasePlayer : : TabulateAmmo ( )
{
ammo_9mm = AmmoInventory ( GetAmmoIndex ( " 9mm " ) ) ;
ammo_357 = AmmoInventory ( GetAmmoIndex ( " 357 " ) ) ;
ammo_argrens = AmmoInventory ( GetAmmoIndex ( " ARgrenades " ) ) ;
ammo_bolts = AmmoInventory ( GetAmmoIndex ( " bolts " ) ) ;
ammo_buckshot = AmmoInventory ( GetAmmoIndex ( " buckshot " ) ) ;
ammo_rockets = AmmoInventory ( GetAmmoIndex ( " rockets " ) ) ;
ammo_uranium = AmmoInventory ( GetAmmoIndex ( " uranium " ) ) ;
ammo_hornets = AmmoInventory ( GetAmmoIndex ( " Hornets " ) ) ;
}
/*
= = = = = = = = = = =
WaterMove
= = = = = = = = = = = =
*/
# define AIRTIME 12 // lung full of air lasts this many seconds
void CBasePlayer : : WaterMove ( )
{
int air ;
if ( pev - > movetype = = MOVETYPE_NOCLIP )
return ;
if ( pev - > health < 0 )
return ;
// waterlevel 0 - not in water
// waterlevel 1 - feet in water
// waterlevel 2 - waist in water
// waterlevel 3 - head in water
if ( pev - > waterlevel ! = 3 )
{
// not underwater
// play 'up for air' sound
if ( pev - > air_finished < gpGlobals - > time )
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pl_wade1.wav " , 1 , ATTN_NORM ) ;
else if ( pev - > air_finished < gpGlobals - > time + 9 )
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pl_wade2.wav " , 1 , ATTN_NORM ) ;
pev - > air_finished = gpGlobals - > time + AIRTIME ;
pev - > dmg = 2 ;
// if we took drowning damage, give it back slowly
if ( m_idrowndmg > m_idrownrestored )
{
// set drowning damage bit. hack - dmg_drownrecover actually
// makes the time based damage code 'give back' health over time.
// make sure counter is cleared so we start count correctly.
// NOTE: this actually causes the count to continue restarting
// until all drowning damage is healed.
m_bitsDamageType | = DMG_DROWNRECOVER ;
m_bitsDamageType & = ~ DMG_DROWN ;
m_rgbTimeBasedDamage [ itbd_DrownRecover ] = 0 ;
}
}
else
{ // fully under water
// stop restoring damage while underwater
m_bitsDamageType & = ~ DMG_DROWNRECOVER ;
m_rgbTimeBasedDamage [ itbd_DrownRecover ] = 0 ;
if ( pev - > air_finished < gpGlobals - > time ) // drown!
{
if ( pev - > pain_finished < gpGlobals - > time )
{
// take drowning damage
pev - > dmg + = 1 ;
if ( pev - > dmg > 5 )
pev - > dmg = 5 ;
TakeDamage ( VARS ( eoNullEntity ) , VARS ( eoNullEntity ) , pev - > dmg , DMG_DROWN ) ;
pev - > pain_finished = gpGlobals - > time + 1 ;
// track drowning damage, give it back when
// player finally takes a breath
m_idrowndmg + = pev - > dmg ;
}
}
else
{
m_bitsDamageType & = ~ DMG_DROWN ;
}
}
if ( ! pev - > waterlevel )
{
if ( FBitSet ( pev - > flags , FL_INWATER ) )
{
ClearBits ( pev - > flags , FL_INWATER ) ;
}
return ;
}
// make bubbles
air = ( int ) ( pev - > air_finished - gpGlobals - > time ) ;
if ( ! RANDOM_LONG ( 0 , 0x1f ) & & RANDOM_LONG ( 0 , AIRTIME - 1 ) > = air )
{
switch ( RANDOM_LONG ( 0 , 3 ) )
{
case 0 : EMIT_SOUND ( ENT ( pev ) , CHAN_BODY , " player/pl_swim1.wav " , 0.8 , ATTN_NORM ) ; break ;
case 1 : EMIT_SOUND ( ENT ( pev ) , CHAN_BODY , " player/pl_swim2.wav " , 0.8 , ATTN_NORM ) ; break ;
case 2 : EMIT_SOUND ( ENT ( pev ) , CHAN_BODY , " player/pl_swim3.wav " , 0.8 , ATTN_NORM ) ; break ;
case 3 : EMIT_SOUND ( ENT ( pev ) , CHAN_BODY , " player/pl_swim4.wav " , 0.8 , ATTN_NORM ) ; break ;
}
}
if ( pev - > watertype = = CONTENT_LAVA ) // do damage
{
if ( pev - > dmgtime < gpGlobals - > time )
TakeDamage ( VARS ( eoNullEntity ) , VARS ( eoNullEntity ) , 10 * pev - > waterlevel , DMG_BURN ) ;
}
else if ( pev - > watertype = = CONTENT_SLIME ) // do damage
{
pev - > dmgtime = gpGlobals - > time + 1 ;
TakeDamage ( VARS ( eoNullEntity ) , VARS ( eoNullEntity ) , 4 * pev - > waterlevel , DMG_ACID ) ;
}
if ( ! FBitSet ( pev - > flags , FL_INWATER ) )
{
SetBits ( pev - > flags , FL_INWATER ) ;
pev - > dmgtime = 0 ;
}
}
void CBasePlayer : : InitPlayerFromSpawn ( edict_t * inSpawn )
{
if ( ! FNullEnt ( inSpawn ) )
{
int theOffset = AvHMUGetOriginOffsetForUser3 ( AvHUser3 ( this - > pev - > iuser3 ) ) ;
this - > pev - > origin = VARS ( inSpawn ) - > origin + Vector ( 0 , 0 , theOffset ) ;
this - > pev - > v_angle = VARS ( inSpawn ) - > v_angle ;
this - > pev - > velocity = g_vecZero ;
this - > pev - > angles = VARS ( inSpawn ) - > angles ;
this - > pev - > punchangle = g_vecZero ;
this - > pev - > fixangle = TRUE ;
}
}
// TRUE if the player is attached to a ladder
BOOL CBasePlayer : : IsOnLadder ( void )
{
return ( pev - > movetype = = MOVETYPE_FLY ) ;
}
void CBasePlayer : : PlayerDeathThink ( void )
{
float flForward ;
if ( FBitSet ( pev - > flags , FL_ONGROUND ) )
{
flForward = pev - > velocity . Length ( ) - 20 ;
if ( flForward < = 0 )
pev - > velocity = g_vecZero ;
else
pev - > velocity = flForward * pev - > velocity . Normalize ( ) ;
}
if ( HasWeapons ( ) )
{
// we drop the guns here because weapons that have an area effect and can kill their user
// will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
// player class sometimes is freed. It's safer to manipulate the weapons once we know
// we aren't calling into any of their code anymore through the player pointer.
PackDeadPlayerItems ( ) ;
// Assumes that all players have at least one weapon when they die
//SpawnClientSideCorpse();
}
if ( pev - > modelindex & & ( ! m_fSequenceFinished ) & & ( pev - > deadflag = = DEAD_DYING ) )
{
StudioFrameAdvance ( ) ;
m_iRespawnFrames + + ; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands
if ( m_iRespawnFrames < 120 ) // Animations should be no longer than this
return ;
}
// once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore
// this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn
if ( pev - > movetype ! = MOVETYPE_NONE & & FBitSet ( pev - > flags , FL_ONGROUND ) )
pev - > movetype = MOVETYPE_NONE ;
if ( pev - > deadflag = = DEAD_DYING )
pev - > deadflag = DEAD_DEAD ;
StopAnimation ( ) ;
pev - > effects | = EF_NOINTERP ;
pev - > framerate = 0.0 ;
BOOL fAnyButtonDown = ( pev - > button & ~ IN_SCORE ) ;
// wait for all buttons released
if ( pev - > deadflag = = DEAD_DEAD )
{
//if (fAnyButtonDown)
// return;
//if ( g_pGameRules->FPlayerCanRespawn( this ) )
//{
m_fDeadTime = gpGlobals - > time ;
pev - > deadflag = DEAD_RESPAWNABLE ;
//}
return ;
}
// if the player has been dead for one second longer than allowed by forcerespawn,
// forcerespawn isn't on. Send the player off to an intermission camera until they
// choose to respawn.
//if ( g_pGameRules->IsMultiplayer() && ( gpGlobals->time > (m_fDeadTime + 6) ) && !(m_afPhysicsFlags & PFLAG_OBSERVER) )
//{
// // go to dead camera.
// StartDeathCam();
//}
// From Counter-strike
// if the player has been dead for one second longer than allowed by forcerespawn,
// forcerespawn isn't on. Send the player off to an intermission camera until they
// choose to respawn.
// if ( g_pGameRules->IsMultiplayer() &&
// ( gpGlobals->time > (m_fDeadTime + 3) ) &&
// !( m_afPhysicsFlags & PFLAG_OBSERVER) )
// {
// //Send message to everybody to spawn a corpse.
// SpawnClientSideCorpse();
//
// // go to dead camera.
// //StartDeathCam();
// }
const float kMinDeathTime = .5f ;
// wait for any button down, or mp_forcerespawn is set and the respawn time is up
if ( ( ! fAnyButtonDown | | ( gpGlobals - > time < ( m_fDeadTime + kMinDeathTime ) ) )
& & ! ( g_pGameRules - > IsMultiplayer ( ) & & forcerespawn . value > 0 & & ( gpGlobals - > time > ( m_fDeadTime + kMinDeathTime ) ) ) )
return ;
pev - > button = 0 ;
m_iRespawnFrames = 0 ;
// Player is done with the death cam, continue
AvHPlayer * thePlayer = dynamic_cast < AvHPlayer * > ( this ) ;
AvHGamerules * theGameRules = dynamic_cast < AvHGamerules * > ( g_pGameRules ) ;
theGameRules - > PlayerDeathEnd ( thePlayer ) ;
}
//=========================================================
// StartDeathCam - find an intermission spot and send the
// player off into observer mode
//=========================================================
void CBasePlayer : : StartDeathCam ( void )
{
edict_t * pSpot , * pNewSpot ;
int iRand ;
if ( pev - > view_ofs = = g_vecZero )
{
// don't accept subsequent attempts to StartDeathCam()
return ;
}
pSpot = FIND_ENTITY_BY_CLASSNAME ( NULL , " info_intermission " ) ;
if ( ! FNullEnt ( pSpot ) )
{
// at least one intermission spot in the world.
iRand = RANDOM_LONG ( 0 , 3 ) ;
while ( iRand > 0 )
{
pNewSpot = FIND_ENTITY_BY_CLASSNAME ( pSpot , " info_intermission " ) ;
if ( pNewSpot )
{
pSpot = pNewSpot ;
}
iRand - - ;
}
CopyToBodyQue ( pev ) ;
StartObserver ( pSpot - > v . origin , pSpot - > v . v_angle ) ;
}
else
{
// no intermission spot. Push them up in the air, looking down at their corpse
TraceResult tr ;
CopyToBodyQue ( pev ) ;
UTIL_TraceLine ( pev - > origin , pev - > origin + Vector ( 0 , 0 , 128 ) , ignore_monsters , edict ( ) , & tr ) ;
StartObserver ( tr . vecEndPos , UTIL_VecToAngles ( tr . vecEndPos - pev - > origin ) ) ;
return ;
}
}
void CBasePlayer : : StartObserver ( Vector vecPosition , Vector vecViewAngle )
{
// m_afPhysicsFlags |= PFLAG_OBSERVER;
//
// pev->view_ofs = g_vecZero;
// pev->angles = pev->v_angle = vecViewAngle;
// pev->fixangle = TRUE;
// pev->solid = SOLID_NOT;
// pev->takedamage = DAMAGE_NO;
// pev->movetype = MOVETYPE_NONE;
// pev->modelindex = 0;
// UTIL_SetOrigin( pev, vecPosition );
m_iHideHUD | = ( HIDEHUD_HEALTH | HIDEHUD_WEAPONS ) ;
m_afPhysicsFlags | = PFLAG_OBSERVER ;
pev - > effects = EF_NODRAW ;
pev - > view_ofs = g_vecZero ;
pev - > angles = pev - > v_angle = vecViewAngle ;
pev - > fixangle = TRUE ;
pev - > solid = SOLID_NOT ;
pev - > takedamage = DAMAGE_NO ;
pev - > movetype = MOVETYPE_NONE ;
UTIL_SetOrigin ( pev , vecPosition ) ;
ClearBits ( m_afPhysicsFlags , PFLAG_DUCKING ) ;
ClearBits ( pev - > flags , FL_DUCKING ) ;
// pev->flags = FL_CLIENT | FL_SPECTATOR; // Should we set Spectator flag? Or is it reserver for people connecting with spectator 1?
pev - > deadflag = DEAD_RESPAWNABLE ;
// Tell the physics code that this player's now in observer mode
Observer_SetMode ( this - > GetDefaultSpectatingMode ( ) ) ;
Observer_SpectatePlayer ( this - > GetDefaultSpectatingTarget ( ) ) ;
m_flNextObserverInput = 0 ;
}
void CBasePlayer : : StopObserver ( )
{
this - > pev - > solid = SOLID_SLIDEBOX ;
this - > pev - > effects = 0 ;
this - > pev - > takedamage = DAMAGE_YES ;
this - > pev - > movetype = MOVETYPE_WALK ;
ClearBits ( this - > m_afPhysicsFlags , PFLAG_OBSERVER ) ;
ClearBits ( this - > m_afPhysicsFlags , PFLAG_DUCKING ) ;
ClearBits ( this - > pev - > flags , FL_DUCKING ) ;
this - > pev - > iuser1 = this - > pev - > iuser2 = 0 ;
}
//
// PlayerUse - handles USE keypress
//
# define PLAYER_SEARCH_RADIUS (float)64
void CBasePlayer : : ClearKeys ( )
{
// clear attack/use commands from player
this - > m_afButtonPressed = 0 ;
this - > pev - > button = 0 ;
this - > m_afButtonReleased = 0 ;
}
void CBasePlayer : : PlayerUse ( void )
{
AvHPlayer * theAvHPlayer = dynamic_cast < AvHPlayer * > ( this ) ;
// Was use pressed or released?
if ( ! ( ( pev - > button | m_afButtonPressed | m_afButtonReleased ) & IN_USE ) )
return ;
//: Dont do this on commanders to prevent phantom use sounds.
if ( theAvHPlayer - > GetIsInTopDownMode ( ) )
return ;
// Hit Use on a train?
if ( m_afButtonPressed & IN_USE )
{
if ( m_pTank ! = NULL )
{
// Stop controlling the tank
// TODO: Send HUD Update
m_pTank - > Use ( this , this , USE_OFF , 0 ) ;
m_pTank = NULL ;
return ;
}
else
{
if ( m_afPhysicsFlags & PFLAG_ONTRAIN )
{
m_afPhysicsFlags & = ~ PFLAG_ONTRAIN ;
m_iTrain = TRAIN_NEW | TRAIN_OFF ;
return ;
}
else
{ // Start controlling the train!
CBaseEntity * pTrain = CBaseEntity : : Instance ( pev - > groundentity ) ;
if ( pTrain & & ! ( pev - > button & IN_JUMP ) & & FBitSet ( pev - > flags , FL_ONGROUND ) & & ( pTrain - > ObjectCaps ( ) & FCAP_DIRECTIONAL_USE ) & & pTrain - > OnControls ( pev ) )
{
m_afPhysicsFlags | = PFLAG_ONTRAIN ;
m_iTrain = TrainSpeed ( pTrain - > pev - > speed , pTrain - > pev - > impulse ) ;
m_iTrain | = TRAIN_NEW ;
EMIT_SOUND ( ENT ( pev ) , CHAN_ITEM , " plats/train_use1.wav " , 0.8 , ATTN_NORM ) ;
return ;
}
}
}
}
CBaseEntity * pObject = NULL ;
CBaseEntity * pClosest = NULL ;
Vector vecLOS ;
float flMaxDot = VIEW_FIELD_NARROW ;
float flDot ;
UTIL_MakeVectors ( pev - > v_angle ) ; // so we know which way we are facing
while ( ( pObject = UTIL_FindEntityInSphere ( pObject , pev - > origin , PLAYER_SEARCH_RADIUS ) ) ! = NULL )
{
if ( pObject - > ObjectCaps ( ) & ( FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE ) )
{
// !!!PERFORMANCE- should this check be done on a per case basis AFTER we've determined that
// this object is actually usable? This dot is being done for every object within PLAYER_SEARCH_RADIUS
// when player hits the use key. How many objects can be in that area, anyway? (sjb)
vecLOS = ( VecBModelOrigin ( pObject - > pev ) - ( pev - > origin + pev - > view_ofs ) ) ;
// This essentially moves the origin of the target to the corner nearest the player to test to see
// if it's "hull" is in the view cone
vecLOS = UTIL_ClampVectorToBox ( vecLOS , pObject - > pev - > size * 0.5 ) ;
flDot = DotProduct ( vecLOS , gpGlobals - > v_forward ) ;
if ( flDot > flMaxDot )
{ // only if the item is in front of the user
pClosest = pObject ;
flMaxDot = flDot ;
// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot );
}
// ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot );
}
}
pObject = pClosest ;
// Add los test for aliens looking at hives.
if ( pObject = = NULL & & AvHGetIsAlien ( this - > pev - > iuser3 ) ) {
Vector vecAiming = gpGlobals - > v_forward ;
Vector vecSrc = this - > GetGunPosition ( ) + vecAiming ;
Vector vecEnd = vecSrc + vecAiming * 800 ;
TraceResult theTraceResult ;
UTIL_TraceLine ( vecSrc , vecEnd , dont_ignore_monsters , this - > edict ( ) , & theTraceResult ) ;
edict_t * theEntityHit = theTraceResult . pHit ;
AvHHive * theHive = dynamic_cast < AvHHive * > ( CBaseEntity : : Instance ( theEntityHit ) ) ;
if ( theHive ) {
float the2DDistance = VectorDistance2D ( this - > pev - > origin , theHive - > pev - > origin ) ;
// Enabled state is true
if ( the2DDistance < = 150.0 & & ( this - > pev - > origin < theHive - > pev - > origin ) )
{
pObject = theHive ;
}
}
}
// Found an object
if ( pObject )
{
//!!!UNDONE: traceline here to prevent USEing buttons through walls
int caps = pObject - > ObjectCaps ( ) ;
if ( m_afButtonPressed & IN_USE )
{
//EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_select.wav", 0.4, ATTN_NORM);
const char * theSound = AvHSHUGetCommonSoundName ( theAvHPlayer - > GetIsAlien ( ) , WEAPON_SOUND_SELECT ) ;
EMIT_SOUND ( ENT ( pev ) , CHAN_ITEM , theSound , 0.4 , ATTN_NORM ) ;
}
if ( ( ( pev - > button & IN_USE ) & & ( caps & FCAP_CONTINUOUS_USE ) ) | |
( ( m_afButtonPressed & IN_USE ) & & ( caps & ( FCAP_IMPULSE_USE | FCAP_ONOFF_USE ) ) ) )
{
if ( caps & FCAP_CONTINUOUS_USE )
m_afPhysicsFlags | = PFLAG_USING ;
pObject - > Use ( this , this , USE_SET , 1 ) ;
}
// UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away
else if ( ( m_afButtonReleased & IN_USE ) & & ( pObject - > ObjectCaps ( ) & FCAP_ONOFF_USE ) ) // BUGBUG This is an "off" use
{
pObject - > Use ( this , this , USE_SET , 0 ) ;
}
}
else
{
if ( m_afButtonPressed & IN_USE )
{
//EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_denyselect.wav", 0.4, ATTN_NORM);
const char * theSound = AvHSHUGetCommonSoundName ( theAvHPlayer - > GetIsAlien ( ) , WEAPON_SOUND_DENYSELECT ) ;
EMIT_SOUND ( ENT ( pev ) , CHAN_ITEM , theSound , 0.4 , ATTN_NORM ) ;
}
}
}
void CBasePlayer : : Jump ( )
{
Vector vecWallCheckDir ; // direction we're tracing a line to find a wall when walljumping
Vector vecAdjustedVelocity ;
Vector vecSpot ;
TraceResult tr ;
if ( FBitSet ( pev - > flags , FL_WATERJUMP ) )
return ;
if ( pev - > waterlevel > = 2 )
{
return ;
}
// jump velocity is sqrt( height * gravity * 2)
// If this isn't the first frame pressing the jump button, break out.
if ( ! FBitSet ( m_afButtonPressed , IN_JUMP ) )
return ; // don't pogo stick
if ( ! ( pev - > flags & FL_ONGROUND ) | | ! pev - > groundentity )
{
return ;
}
// many features in this function use v_forward, so makevectors now.
UTIL_MakeVectors ( pev - > angles ) ;
// ClearBits(pev->flags, FL_ONGROUND); // don't stairwalk
SetAnimation ( PLAYER_JUMP ) ;
if ( m_fLongJump & &
( pev - > button & IN_DUCK ) & &
( pev - > flDuckTime > 0 ) & &
pev - > velocity . Length ( ) > 50 )
{
SetAnimation ( PLAYER_SUPERJUMP ) ;
}
// If you're standing on a conveyor, add it's velocity to yours (for momentum)
entvars_t * pevGround = VARS ( pev - > groundentity ) ;
if ( pevGround & & ( pevGround - > flags & FL_CONVEYOR ) )
{
pev - > velocity = pev - > velocity + pev - > basevelocity ;
}
}
// This is a glorious hack to find free space when you've crouched into some solid space
// Our crouching collisions do not work correctly for some reason and this is easier
// than fixing the problem :(
void FixPlayerCrouchStuck ( edict_t * pPlayer )
{
TraceResult trace ;
// Move up as many as 18 pixels if the player is stuck.
for ( int i = 0 ; i < 18 ; i + + )
{
UTIL_TraceHull ( pPlayer - > v . origin , pPlayer - > v . origin , dont_ignore_monsters , head_hull , pPlayer , & trace ) ;
if ( trace . fStartSolid )
pPlayer - > v . origin . z + + ;
else
break ;
}
}
void CBasePlayer : : Duck ( )
{
if ( pev - > button & IN_DUCK )
{
if ( m_IdealActivity ! = ACT_LEAP )
{
SetAnimation ( PLAYER_WALK ) ;
}
}
}
//
// ID's player as such.
//
int CBasePlayer : : Classify ( void )
{
return CLASS_PLAYER ;
}
void CBasePlayer : : AddPoints ( int score , BOOL bAllowNegativeScore )
{
// Positive score always adds
if ( score < 0 )
{
if ( ! bAllowNegativeScore )
{
if ( pev - > frags < 0 ) // Can't go more negative
return ;
if ( - score > pev - > frags ) // Will this go negative?
{
score = - pev - > frags ; // Sum will be 0
}
}
}
pev - > frags + = score ;
}
void CBasePlayer : : EffectivePlayerClassChanged ( )
{
}
void CBasePlayer : : NeedsTeamUpdate ( )
{
}
void CBasePlayer : : SendTeamUpdate ( )
{
}
void CBasePlayer : : AddPointsToTeam ( int score , BOOL bAllowNegativeScore )
{
int index = entindex ( ) ;
for ( int i = 1 ; i < = gpGlobals - > maxClients ; i + + )
{
CBaseEntity * pPlayer = UTIL_PlayerByIndex ( i ) ;
if ( pPlayer & & i ! = index )
{
if ( g_pGameRules - > PlayerRelationship ( this , pPlayer ) = = GR_TEAMMATE )
{
pPlayer - > AddPoints ( score , bAllowNegativeScore ) ;
}
}
}
}
//Player ID
void CBasePlayer : : InitStatusBar ( )
{
m_flStatusBarDisappearDelay = 0 ;
m_SbarString1 [ 0 ] = m_SbarString0 [ 0 ] = 0 ;
}
void CBasePlayer : : UpdateStatusBar ( )
{
int newSBarState [ SBAR_END ] ;
char sbuf0 [ SBAR_STRING_SIZE ] ;
char sbuf1 [ SBAR_STRING_SIZE ] ;
int i ;
for ( i = 0 ; i < SBAR_END ; + + i )
{
newSBarState [ i ] = - 1 ;
}
//memset( newSBarState, 0, sizeof(newSBarState) );
strcpy ( sbuf0 , m_SbarString0 ) ;
strcpy ( sbuf1 , m_SbarString1 ) ;
// Find an ID Target
TraceResult tr ;
UTIL_MakeVectors ( pev - > v_angle + pev - > punchangle ) ;
Vector vecSrc = EyePosition ( ) ;
Vector vecEnd = vecSrc + ( gpGlobals - > v_forward * MAX_ID_RANGE ) ;
UTIL_TraceLine ( vecSrc , vecEnd , dont_ignore_monsters , edict ( ) , & tr ) ;
bool theIsCombatMode = GetGameRules ( ) - > GetIsCombatMode ( ) ;
int theMaxEnumToSend = SBAR_ID_TARGETARMOR ;
if ( theIsCombatMode )
{
theMaxEnumToSend = SBAR_ID_TARGETLEVEL ;
}
if ( tr . flFraction ! = 1.0 )
{
if ( ! FNullEnt ( tr . pHit ) )
{
CBaseEntity * pEntity = CBaseEntity : : Instance ( tr . pHit ) ;
if ( ! pEntity )
return ;
if ( pEntity - > Classify ( ) = = CLASS_PLAYER )
{
newSBarState [ SBAR_ID_TARGETNAME ] = ENTINDEX ( pEntity - > edict ( ) ) ;
// Name Health: X% Armor: Y%
//strcpy( sbuf1, "1 %p1\n2 Health: %i2%%\n3 Armor: %i3%%" );
// (health: X armor: Y)
if ( ! theIsCombatMode )
{
strcpy ( sbuf1 , " 2 (health: %i2%% \n 3 armor: %i3%%) " ) ;
}
else
{
strcpy ( sbuf1 , " 2 (health: %i2%% \n 3 armor: %i3%% \n 4 level: %i4) " ) ;
}
// allies and medics get to see the targets health
if ( g_pGameRules - > PlayerRelationship ( this , pEntity ) = = GR_TEAMMATE )
{
int theLevel = 1 ;
AvHPlayer * thePlayer = dynamic_cast < AvHPlayer * > ( pEntity ) ;
if ( thePlayer )
{
theLevel = thePlayer - > GetExperienceLevel ( ) ;
}
//newSBarState[ SBAR_ID_TARGETHEALTH ] = 100 * (pEntity->pev->health / pEntity->pev->max_health);
//newSBarState[ SBAR_ID_TARGETARMOR ] = pEntity->pev->armorvalue; //No need to get it % based since 100 it's the max.
float theHealthPercent = pEntity - > pev - > health / AvHPlayerUpgrade : : GetMaxHealth ( pEntity - > pev - > iuser4 , ( AvHUser3 ) pEntity - > pev - > iuser3 , theLevel ) ;
float theArmorPercent = pEntity - > pev - > armorvalue / AvHPlayerUpgrade : : GetMaxArmorLevel ( pEntity - > pev - > iuser4 , ( AvHUser3 ) pEntity - > pev - > iuser3 ) ;
newSBarState [ SBAR_ID_TARGETHEALTH ] = max ( theHealthPercent * 100 + 0.5f , 0.0f ) ;
newSBarState [ SBAR_ID_TARGETARMOR ] = max ( theArmorPercent * 100 + 0.5f , 0.0f ) ;
newSBarState [ SBAR_ID_TARGETLEVEL ] = theLevel ;
}
}
else
{
bool theSuccess = false ;
// Show hive health for friendlies
if ( this - > pev - > team = = pEntity - > pev - > team )
{
AvHUser3 theUser3 = ( AvHUser3 ) pEntity - > pev - > iuser3 ;
switch ( theUser3 )
{
// Place user3s to draw here
case AVH_USER3_HIVE :
theSuccess = true ;
break ;
}
}
if ( theSuccess )
{
newSBarState [ SBAR_ID_TARGETNAME ] = ENTINDEX ( pEntity - > edict ( ) ) ;
strcpy ( sbuf1 , " 2 (health: %i2%%) \n " ) ;
float theHealthPercentage = pEntity - > pev - > health / pEntity - > pev - > max_health ;
newSBarState [ SBAR_ID_TARGETHEALTH ] = max ( theHealthPercentage * 100 + 0.5f , 0.0f ) ;
}
}
m_flStatusBarDisappearDelay = gpGlobals - > time + 1.0 ;
}
else if ( m_flStatusBarDisappearDelay > gpGlobals - > time )
{
// hold the values for a short amount of time after viewing the object
newSBarState [ SBAR_ID_TARGETNAME ] = m_izSBarState [ SBAR_ID_TARGETNAME ] ;
newSBarState [ SBAR_ID_TARGETHEALTH ] = m_izSBarState [ SBAR_ID_TARGETHEALTH ] ;
newSBarState [ SBAR_ID_TARGETARMOR ] = m_izSBarState [ SBAR_ID_TARGETARMOR ] ;
newSBarState [ SBAR_ID_TARGETLEVEL ] = m_izSBarState [ SBAR_ID_TARGETLEVEL ] ;
}
}
BOOL bForceResend = FALSE ;
if ( strcmp ( sbuf0 , m_SbarString0 ) )
{
NetMsg_StatusText ( pev , 0 , string ( sbuf0 ) ) ;
strcpy ( m_SbarString0 , sbuf0 ) ;
// make sure everything's resent
bForceResend = TRUE ;
}
if ( strcmp ( sbuf1 , m_SbarString1 ) )
{
NetMsg_StatusText ( pev , 1 , string ( sbuf1 ) ) ;
strcpy ( m_SbarString1 , sbuf1 ) ;
// make sure everything's resent
bForceResend = TRUE ;
}
// Check values and send if they don't match
for ( i = 1 ; i < = theMaxEnumToSend ; i + + )
{
if ( newSBarState [ i ] ! = m_izSBarState [ i ] | | bForceResend )
{
NetMsg_StatusValue ( pev , i , newSBarState [ i ] ) ;
m_izSBarState [ i ] = newSBarState [ i ] ;
}
}
}
# define CLIMB_SHAKE_FREQUENCY 22 // how many frames in between screen shakes when climbing
# define MAX_CLIMB_SPEED 200 // fastest vertical climbing speed possible
# define CLIMB_SPEED_DEC 15 // climbing deceleration rate
# define CLIMB_PUNCH_X -7 // how far to 'punch' client X axis when climbing
# define CLIMB_PUNCH_Z 7 // how far to 'punch' client Z axis when climbing
void CBasePlayer : : PreThink ( void )
{
int buttonsChanged = ( m_afButtonLast ^ pev - > button ) ; // These buttons have changed this frame
// Debounced button codes for pressed/released
// UNDONE: Do we need auto-repeat?
m_afButtonPressed = buttonsChanged & pev - > button ; // The changed ones still down are "pressed"
m_afButtonReleased = buttonsChanged & ( ~ pev - > button ) ; // The ones not down are "released"
g_pGameRules - > PlayerThink ( this ) ;
if ( g_fGameOver )
return ; // intermission or finale
UTIL_MakeVectors ( pev - > v_angle ) ; // is this still used?
ItemPreFrame ( ) ;
WaterMove ( ) ;
if ( g_pGameRules & & g_pGameRules - > FAllowFlashlight ( ) )
m_iHideHUD & = ~ HIDEHUD_FLASHLIGHT ;
else
m_iHideHUD | = HIDEHUD_FLASHLIGHT ;
// JOHN: checks if new client data (for HUD and view control) needs to be sent to the client
UpdateClientData ( ) ;
CheckTimeBasedDamage ( ) ;
// Observer Button Handling
if ( this - > IsObserver ( ) )
{
Observer_HandleButtons ( ) ;
pev - > impulse = 0 ;
return ;
}
CheckSuitUpdate ( ) ;
if ( pev - > deadflag > = DEAD_DYING )
{
PlayerDeathThink ( ) ;
return ;
}
// So the correct flags get sent to client asap.
//
if ( m_afPhysicsFlags & PFLAG_ONTRAIN )
pev - > flags | = FL_ONTRAIN ;
else
pev - > flags & = ~ FL_ONTRAIN ;
// Train speed control
if ( m_afPhysicsFlags & PFLAG_ONTRAIN )
{
CBaseEntity * pTrain = CBaseEntity : : Instance ( pev - > groundentity ) ;
float vel ;
if ( ! pTrain )
{
TraceResult trainTrace ;
// Maybe this is on the other side of a level transition
UTIL_TraceLine ( pev - > origin , pev - > origin + Vector ( 0 , 0 , - 38 ) , ignore_monsters , ENT ( pev ) , & trainTrace ) ;
// HACKHACK - Just look for the func_tracktrain classname
if ( trainTrace . flFraction ! = 1.0 & & trainTrace . pHit )
pTrain = CBaseEntity : : Instance ( trainTrace . pHit ) ;
if ( ! pTrain | | ! ( pTrain - > ObjectCaps ( ) & FCAP_DIRECTIONAL_USE ) | | ! pTrain - > OnControls ( pev ) )
{
//ALERT( at_error, "In train mode with no train!\n" );
m_afPhysicsFlags & = ~ PFLAG_ONTRAIN ;
m_iTrain = TRAIN_NEW | TRAIN_OFF ;
return ;
}
}
else if ( ! FBitSet ( pev - > flags , FL_ONGROUND ) | | FBitSet ( pTrain - > pev - > spawnflags , SF_TRACKTRAIN_NOCONTROL ) | | ( pev - > button & ( IN_MOVELEFT | IN_MOVERIGHT ) ) )
{
// Turn off the train if you jump, strafe, or the train controls go dead
m_afPhysicsFlags & = ~ PFLAG_ONTRAIN ;
m_iTrain = TRAIN_NEW | TRAIN_OFF ;
return ;
}
pev - > velocity = g_vecZero ;
vel = 0 ;
if ( m_afButtonPressed & IN_FORWARD )
{
vel = 1 ;
pTrain - > Use ( this , this , USE_SET , ( float ) vel ) ;
}
else if ( m_afButtonPressed & IN_BACK )
{
vel = - 1 ;
pTrain - > Use ( this , this , USE_SET , ( float ) vel ) ;
}
if ( vel )
{
m_iTrain = TrainSpeed ( pTrain - > pev - > speed , pTrain - > pev - > impulse ) ;
m_iTrain | = TRAIN_ACTIVE | TRAIN_NEW ;
}
} else if ( m_iTrain & TRAIN_ACTIVE )
m_iTrain = TRAIN_NEW ; // turn off train
if ( pev - > button & IN_JUMP )
{
// If on a ladder, jump off the ladder
// else Jump
Jump ( ) ;
}
// If trying to duck, already ducked, or in the process of ducking
if ( ( pev - > button & IN_DUCK ) | | FBitSet ( pev - > flags , FL_DUCKING ) | | ( m_afPhysicsFlags & PFLAG_DUCKING ) ) {
if ( AvHMUGetCanDuck ( this - > pev - > iuser3 ) )
Duck ( ) ;
}
if ( ! FBitSet ( pev - > flags , FL_ONGROUND ) )
{
m_flFallVelocity = - pev - > velocity . z ;
}
// StudioFrameAdvance( );//!!!HACKHACK!!! Can't be hit by traceline when not animating?
// Clear out ladder pointer
m_hEnemy = NULL ;
if ( m_afPhysicsFlags & PFLAG_ONBARNACLE )
{
pev - > velocity = g_vecZero ;
}
}
/* Time based Damage works as follows:
1 ) There are several types of timebased damage :
# define DMG_PARALYZE (1 << 14) // slows affected creature down
# define DMG_NERVEGAS (1 << 15) // nerve toxins, very bad
# define DMG_POISON (1 << 16) // blood poisioning
# define DMG_RADIATION (1 << 17) // radiation exposure
# define DMG_DROWNRECOVER (1 << 18) // drown recovery
# define DMG_ACID (1 << 19) // toxic chemicals or acid burns
# define DMG_SLOWBURN (1 << 20) // in an oven
# define DMG_SLOWFREEZE (1 << 21) // in a subzero freezer
2 ) A new hit inflicting tbd restarts the tbd counter - each monster has an 8 bit counter ,
per damage type . The counter is decremented every second , so the maximum time
an effect will last is 255 / 60 = 4.25 minutes . Of course , staying within the radius
of a damaging effect like fire , nervegas , radiation will continually reset the counter to max .
3 ) Every second that a tbd counter is running , the player takes damage . The damage
is determined by the type of tdb .
Paralyze - 1 / 2 movement rate , 30 second duration .
Nervegas - 5 points per second , 16 second duration = 80 points max dose .
Poison - 2 points per second , 25 second duration = 50 points max dose .
Radiation - 1 point per second , 50 second duration = 50 points max dose .
Drown - 5 points per second , 2 second duration .
Acid / Chemical - 5 points per second , 10 second duration = 50 points max .
Burn - 10 points per second , 2 second duration .
Freeze - 3 points per second , 10 second duration = 30 points max .
4 ) Certain actions or countermeasures counteract the damaging effects of tbds :
Armor / Heater / Cooler - Chemical ( acid ) , burn , freeze all do damage to armor power , then to body
- recharged by suit recharger
Air In Lungs - drowning damage is done to air in lungs first , then to body
- recharged by poking head out of water
- 10 seconds if swiming fast
Air In SCUBA - drowning damage is done to air in tanks first , then to body
- 2 minutes in tanks . Need new tank once empty .
Radiation Syringe - Each syringe full provides protection vs one radiation dosage
Antitoxin Syringe - Each syringe full provides protection vs one poisoning ( nervegas or poison ) .
Health kit - Immediate stop to acid / chemical , fire or freeze damage .
Radiation Shower - Immediate stop to radiation damage , acid / chemical or fire damage .
*/
// If player is taking time based damage, continue doing damage to player -
// this simulates the effect of being poisoned, gassed, dosed with radiation etc -
// anything that continues to do damage even after the initial contact stops.
// Update all time based damage counters, and shut off any that are done.
// The m_bitsDamageType bit MUST be set if any damage is to be taken.
// This routine will detect the initial on value of the m_bitsDamageType
// and init the appropriate counter. Only processes damage every second.
//#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage
//#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval
//#define NERVEGAS_DURATION 16
//#define NERVEGAS_DAMAGE 5.0
//#define POISON_DURATION 25
//#define POISON_DAMAGE 2.0
//#define RADIATION_DURATION 50
//#define RADIATION_DAMAGE 1.0
//#define ACID_DURATION 10
//#define ACID_DAMAGE 5.0
//#define SLOWBURN_DURATION 2
//#define SLOWBURN_DAMAGE 1.0
//#define SLOWFREEZE_DURATION 1.0
//#define SLOWFREEZE_DAMAGE 3.0
/* */
void CBasePlayer : : CheckTimeBasedDamage ( )
{
int i ;
BYTE bDuration = 0 ;
static float gtbdPrev = 0.0 ;
if ( ! ( m_bitsDamageType & DMG_TIMEBASED ) )
return ;
// only check for time based damage approx. every 2 seconds
if ( abs ( gpGlobals - > time - m_tbdPrev ) < 2.0 )
return ;
m_tbdPrev = gpGlobals - > time ;
for ( i = 0 ; i < CDMG_TIMEBASED ; i + + )
{
// make sure bit is set for damage type
if ( m_bitsDamageType & ( DMG_PARALYZE < < i ) )
{
switch ( i )
{
case itbd_Paralyze :
// UNDONE - flag movement as half-speed
bDuration = PARALYZE_DURATION ;
break ;
case itbd_NerveGas :
// TakeDamage(pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC);
bDuration = NERVEGAS_DURATION ;
break ;
case itbd_Poison :
TakeDamage ( pev , pev , POISON_DAMAGE , DMG_GENERIC ) ;
bDuration = POISON_DURATION ;
break ;
case itbd_Radiation :
// TakeDamage(pev, pev, RADIATION_DAMAGE, DMG_GENERIC);
bDuration = RADIATION_DURATION ;
break ;
case itbd_DrownRecover :
// NOTE: this hack is actually used to RESTORE health
// after the player has been drowning and finally takes a breath
if ( m_idrowndmg > m_idrownrestored )
{
int idif = min ( m_idrowndmg - m_idrownrestored , 10 ) ;
TakeHealth ( idif , DMG_GENERIC ) ;
m_idrownrestored + = idif ;
}
bDuration = 4 ; // get up to 5*10 = 50 points back
break ;
case itbd_Acid :
// TakeDamage(pev, pev, ACID_DAMAGE, DMG_GENERIC);
bDuration = ACID_DURATION ;
break ;
case itbd_SlowBurn :
// TakeDamage(pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC);
bDuration = SLOWBURN_DURATION ;
break ;
case itbd_SlowFreeze :
// TakeDamage(pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC);
bDuration = SLOWFREEZE_DURATION ;
break ;
default :
bDuration = 0 ;
}
if ( m_rgbTimeBasedDamage [ i ] )
{
// use up an antitoxin on poison or nervegas after a few seconds of damage
if ( ( ( i = = itbd_NerveGas ) & & ( m_rgbTimeBasedDamage [ i ] < NERVEGAS_DURATION ) ) | |
( ( i = = itbd_Poison ) & & ( m_rgbTimeBasedDamage [ i ] < POISON_DURATION ) ) )
{
if ( m_rgItems [ ITEM_ANTIDOTE ] )
{
m_rgbTimeBasedDamage [ i ] = 0 ;
m_rgItems [ ITEM_ANTIDOTE ] - - ;
SetSuitUpdate ( " !HEV_HEAL4 " , FALSE , SUIT_REPEAT_OK ) ;
}
}
// decrement damage duration, detect when done.
if ( ! m_rgbTimeBasedDamage [ i ] | | - - m_rgbTimeBasedDamage [ i ] = = 0 )
{
m_rgbTimeBasedDamage [ i ] = 0 ;
// if we're done, clear damage bits
m_bitsDamageType & = ~ ( DMG_PARALYZE < < i ) ;
}
}
else
// first time taking this damage type - init damage duration
m_rgbTimeBasedDamage [ i ] = bDuration ;
}
}
}
/*
THE POWER SUIT
The Suit provides 3 main functions : Protection , Notification and Augmentation .
Some functions are automatic , some require power .
The player gets the suit shortly after getting off the train in C1A0 and it stays
with him for the entire game .
Protection
Heat / Cold
When the player enters a hot / cold area , the heating / cooling indicator on the suit
will come on and the battery will drain while the player stays in the area .
After the battery is dead , the player starts to take damage .
This feature is built into the suit and is automatically engaged .
Radiation Syringe
This will cause the player to be immune from the effects of radiation for N seconds . Single use item .
Anti - Toxin Syringe
This will cure the player from being poisoned . Single use item .
Health
Small ( 1 st aid kits , food , etc . )
Large ( boxes on walls )
Armor
The armor works using energy to create a protective field that deflects a
percentage of damage projectile and explosive attacks . After the armor has been deployed ,
it will attempt to recharge itself to full capacity with the energy reserves from the battery .
It takes the armor N seconds to fully charge .
Notification ( via the HUD )
x Health
x Ammo
x Automatic Health Care
Notifies the player when automatic healing has been engaged .
x Geiger counter
Classic Geiger counter sound and status bar at top of HUD
alerts player to dangerous levels of radiation . This is not visible when radiation levels are normal .
x Poison
Armor
Displays the current level of armor .
Augmentation
Reanimation ( w / adrenaline )
Causes the player to come back to life after he has been dead for 3 seconds .
Will not work if player was gibbed . Single use .
Long Jump
Used by hitting the ? ? ? key ( s ) . Caused the player to further than normal .
SCUBA
Used automatically after picked up and after player enters the water .
Works for N seconds . Single use .
Things powered by the battery
Armor
Uses N watts for every M units of damage .
Heat / Cool
Uses N watts for every second in hot / cold area .
Long Jump
Uses N watts for every jump .
Alien Cloak
Uses N watts for each use . Each use lasts M seconds .
Alien Shield
Augments armor . Reduces Armor drain by one half
*/
// if in range of radiation source, ping geiger counter
# define GEIGERDELAY 0.25
void CBasePlayer : : UpdateGeigerCounter ( void )
{
BYTE range ;
// delay per update ie: don't flood net with these msgs
if ( gpGlobals - > time < m_flgeigerDelay )
return ;
m_flgeigerDelay = gpGlobals - > time + GEIGERDELAY ;
// send range to radition source to client
range = ( BYTE ) ( m_flgeigerRange / 4 ) ;
if ( range ! = m_igeigerRangePrev )
{
m_igeigerRangePrev = range ;
NetMsg_GeigerRange ( pev , range ) ;
}
// reset counter and semaphore
if ( ! RANDOM_LONG ( 0 , 3 ) )
m_flgeigerRange = 1000 ;
}
/*
= = = = = = = = = = = = = = = =
CheckSuitUpdate
Play suit update if it ' s time
= = = = = = = = = = = = = = = =
*/
# define SUITUPDATETIME 3.5
# define SUITFIRSTUPDATETIME 0.1
void CBasePlayer : : CheckSuitUpdate ( )
{
int i ;
int isentence = 0 ;
int isearch = m_iSuitPlayNext ;
// Ignore suit updates if no suit
if ( ! ( pev - > weapons & ( 1 < < WEAPON_SUIT ) ) )
return ;
// if in range of radiation source, ping geiger counter
UpdateGeigerCounter ( ) ;
if ( g_pGameRules - > IsMultiplayer ( ) )
{
// don't bother updating HEV voice in multiplayer.
return ;
}
if ( gpGlobals - > time > = m_flSuitUpdate & & m_flSuitUpdate > 0 )
{
// play a sentence off of the end of the queue
for ( i = 0 ; i < CSUITPLAYLIST ; i + + )
{
if ( isentence = m_rgSuitPlayList [ isearch ] )
break ;
if ( + + isearch = = CSUITPLAYLIST )
isearch = 0 ;
}
if ( isentence )
{
m_rgSuitPlayList [ isearch ] = 0 ;
if ( isentence > 0 )
{
// play sentence number
char sentence [ CBSENTENCENAME_MAX + 1 ] ;
strcpy ( sentence , " ! " ) ;
strcat ( sentence , gszallsentencenames [ isentence ] ) ;
EMIT_SOUND_SUIT ( ENT ( pev ) , sentence ) ;
}
else
{
// play sentence group
EMIT_GROUPID_SUIT ( ENT ( pev ) , - isentence ) ;
}
m_flSuitUpdate = gpGlobals - > time + SUITUPDATETIME ;
}
else
// queue is empty, don't check
m_flSuitUpdate = 0 ;
}
}
// add sentence to suit playlist queue. if fgroup is true, then
// name is a sentence group (HEV_AA), otherwise name is a specific
// sentence name ie: !HEV_AA0. If iNoRepeat is specified in
// seconds, then we won't repeat playback of this word or sentence
// for at least that number of seconds.
void CBasePlayer : : SetSuitUpdate ( char * name , int fgroup , int iNoRepeatTime )
{
int i ;
int isentence ;
int iempty = - 1 ;
// Ignore suit updates if no suit
if ( ! ( pev - > weapons & ( 1 < < WEAPON_SUIT ) ) )
return ;
if ( g_pGameRules - > IsMultiplayer ( ) )
{
// due to static channel design, etc. We don't play HEV sounds in multiplayer right now.
return ;
}
// if name == NULL, then clear out the queue
if ( ! name )
{
for ( i = 0 ; i < CSUITPLAYLIST ; i + + )
m_rgSuitPlayList [ i ] = 0 ;
return ;
}
// get sentence or group number
if ( ! fgroup )
{
isentence = SENTENCEG_Lookup ( name , NULL ) ;
if ( isentence < 0 )
return ;
}
else
// mark group number as negative
isentence = - SENTENCEG_GetIndex ( name ) ;
// check norepeat list - this list lets us cancel
// the playback of words or sentences that have already
// been played within a certain time.
for ( i = 0 ; i < CSUITNOREPEAT ; i + + )
{
if ( isentence = = m_rgiSuitNoRepeat [ i ] )
{
// this sentence or group is already in
// the norepeat list
if ( m_rgflSuitNoRepeatTime [ i ] < gpGlobals - > time )
{
// norepeat time has expired, clear it out
m_rgiSuitNoRepeat [ i ] = 0 ;
m_rgflSuitNoRepeatTime [ i ] = 0.0 ;
iempty = i ;
break ;
}
else
{
// don't play, still marked as norepeat
return ;
}
}
// keep track of empty slot
if ( ! m_rgiSuitNoRepeat [ i ] )
iempty = i ;
}
// sentence is not in norepeat list, save if norepeat time was given
if ( iNoRepeatTime )
{
if ( iempty < 0 )
iempty = RANDOM_LONG ( 0 , CSUITNOREPEAT - 1 ) ; // pick random slot to take over
m_rgiSuitNoRepeat [ iempty ] = isentence ;
m_rgflSuitNoRepeatTime [ iempty ] = iNoRepeatTime + gpGlobals - > time ;
}
// find empty spot in queue, or overwrite last spot
m_rgSuitPlayList [ m_iSuitPlayNext + + ] = isentence ;
if ( m_iSuitPlayNext = = CSUITPLAYLIST )
m_iSuitPlayNext = 0 ;
if ( m_flSuitUpdate < = gpGlobals - > time )
{
if ( m_flSuitUpdate = = 0 )
// play queue is empty, don't delay too long before playback
m_flSuitUpdate = gpGlobals - > time + SUITFIRSTUPDATETIME ;
else
m_flSuitUpdate = gpGlobals - > time + SUITUPDATETIME ;
}
}
/*
= = = = = = = = = = = = = = = =
CheckPowerups
Check for turning off powerups
GLOBALS ASSUMED SET : g_ulModelIndexPlayer
= = = = = = = = = = = = = = = =
*/
static void
CheckPowerups ( entvars_t * pev )
{
if ( pev - > health < = 0 )
return ;
//pev->modelindex = g_ulModelIndexPlayer; // don't use eyes
}
//=========================================================
// UpdatePlayerSound - updates the position of the player's
// reserved sound slot in the sound list.
//=========================================================
void CBasePlayer : : UpdatePlayerSound ( void )
{
int iBodyVolume ;
int iVolume ;
CSound * pSound ;
pSound = CSoundEnt : : SoundPointerForIndex ( CSoundEnt : : ClientSoundIndex ( edict ( ) ) ) ;
if ( ! pSound )
{
ALERT ( at_console , " Client lost reserved sound! \n " ) ;
return ;
}
pSound - > m_iType = bits_SOUND_NONE ;
// now calculate the best target volume for the sound. If the player's weapon
// is louder than his body/movement, use the weapon volume, else, use the body volume.
if ( FBitSet ( pev - > flags , FL_ONGROUND ) )
{
iBodyVolume = pev - > velocity . Length ( ) ;
// clamp the noise that can be made by the body, in case a push trigger,
// weapon recoil, or anything shoves the player abnormally fast.
if ( iBodyVolume > 512 )
{
iBodyVolume = 512 ;
}
}
else
{
iBodyVolume = 0 ;
}
if ( pev - > button & IN_JUMP )
{
iBodyVolume + = 100 ;
}
// convert player move speed and actions into sound audible by monsters.
if ( m_iWeaponVolume > iBodyVolume )
{
m_iTargetVolume = m_iWeaponVolume ;
// OR in the bits for COMBAT sound if the weapon is being louder than the player.
pSound - > m_iType | = bits_SOUND_COMBAT ;
}
else
{
m_iTargetVolume = iBodyVolume ;
}
// decay weapon volume over time so bits_SOUND_COMBAT stays set for a while
m_iWeaponVolume - = 250 * gpGlobals - > frametime ;
if ( m_iWeaponVolume < 0 )
{
iVolume = 0 ;
}
// if target volume is greater than the player sound's current volume, we paste the new volume in
// immediately. If target is less than the current volume, current volume is not set immediately to the
// lower volume, rather works itself towards target volume over time. This gives monsters a much better chance
// to hear a sound, especially if they don't listen every frame.
iVolume = pSound - > m_iVolume ;
if ( m_iTargetVolume > iVolume )
{
iVolume = m_iTargetVolume ;
}
else if ( iVolume > m_iTargetVolume )
{
iVolume - = 250 * gpGlobals - > frametime ;
if ( iVolume < m_iTargetVolume )
{
iVolume = 0 ;
}
}
if ( m_fNoPlayerSound )
{
// debugging flag, lets players move around and shoot without monsters hearing.
iVolume = 0 ;
}
if ( gpGlobals - > time > m_flStopExtraSoundTime )
{
// since the extra sound that a weapon emits only lasts for one client frame, we keep that sound around for a server frame or two
// after actual emission to make sure it gets heard.
m_iExtraSoundTypes = 0 ;
}
if ( pSound )
{
pSound - > m_vecOrigin = pev - > origin ;
pSound - > m_iType | = ( bits_SOUND_PLAYER | m_iExtraSoundTypes ) ;
pSound - > m_iVolume = iVolume ;
}
// keep track of virtual muzzle flash
m_iWeaponFlash - = 256 * gpGlobals - > frametime ;
if ( m_iWeaponFlash < 0 )
m_iWeaponFlash = 0 ;
//UTIL_MakeVectors ( pev->angles );
//gpGlobals->v_forward.z = 0;
// Below are a couple of useful little bits that make it easier to determine just how much noise the
// player is making.
// UTIL_ParticleEffect ( pev->origin + gpGlobals->v_forward * iVolume, g_vecZero, 255, 25 );
//ALERT ( at_console, "%d/%d\n", iVolume, m_iTargetVolume );
}
void CBasePlayer : : PostThink ( )
{
if ( g_fGameOver )
goto pt_end ; // intermission or finale
if ( ! IsAlive ( ) )
goto pt_end ;
// Handle Tank controlling
if ( m_pTank ! = NULL )
{ // if they've moved too far from the gun, or selected a weapon, unuse the gun
if ( m_pTank - > OnControls ( pev ) & & ! pev - > weaponmodel )
{
m_pTank - > Use ( this , this , USE_SET , 2 ) ; // try fire the gun
}
else
{ // they've moved off the platform
m_pTank - > Use ( this , this , USE_OFF , 0 ) ;
m_pTank = NULL ;
}
}
// do weapon stuff
ItemPostFrame ( ) ;
// check to see if player landed hard enough to make a sound
// falling farther than half of the maximum safe distance, but not as far a max safe distance will
// play a bootscrape sound, and no damage will be inflicted. Fallling a distance shorter than half
// of maximum safe distance will make no sound. Falling farther than max safe distance will play a
// fallpain sound, and damage will be inflicted based on how far the player fell
if ( ( FBitSet ( pev - > flags , FL_ONGROUND ) ) & & ( pev - > health > 0 ) & & m_flFallVelocity > = PLAYER_FALL_PUNCH_THRESHHOLD )
{
// ALERT ( at_console, "%f\n", m_flFallVelocity );
if ( pev - > watertype = = CONTENT_WATER )
{
// Did he hit the world or a non-moving entity?
// BUG - this happens all the time in water, especially when
// BUG - water has current force
// if ( !pev->groundentity || VARS(pev->groundentity)->velocity.z == 0 )
// EMIT_SOUND(ENT(pev), CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM);
}
// skulks, lerks, fades and jetpackers don't take falling damage
else if ( ( m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED ) & & ( this - > pev - > iuser3 ! = AVH_USER3_ALIEN_PLAYER1 ) & & ( this - > pev - > iuser3 ! = AVH_USER3_ALIEN_PLAYER3 ) & & ( this - > pev - > iuser3 ! = AVH_USER3_ALIEN_PLAYER4 ) & & ( this - > pev - > iuser3 ! = AVH_USER3_ALIEN_EMBRYO ) & & ( ! GetHasUpgrade ( this - > pev - > iuser4 , MASK_UPGRADE_7 ) | | ! ( this - > pev - > iuser3 = = AVH_USER3_MARINE_PLAYER ) ) )
{ // after this point, we start doing damage
float flFallDamage = g_pGameRules - > FlPlayerFallDamage ( this ) ;
if ( flFallDamage > pev - > health )
{ //splat
// note: play on item channel because we play footstep landing on body channel
// : 243 don't play gib sound if being digested
if ( AvHGetIsAlien ( this - > pev - > iuser3 ) | | ! GetHasUpgrade ( this - > pev - > iuser4 , MASK_DIGESTING ) )
EMIT_SOUND ( ENT ( pev ) , CHAN_ITEM , " common/bodysplat.wav " , 1 , ATTN_NORM ) ;
}
if ( flFallDamage > 0 )
{
TakeDamage ( VARS ( eoNullEntity ) , VARS ( eoNullEntity ) , flFallDamage , DMG_FALL ) ;
pev - > punchangle . x = 0 ;
}
}
if ( IsAlive ( ) )
{
SetAnimation ( PLAYER_WALK ) ;
}
}
if ( FBitSet ( pev - > flags , FL_ONGROUND ) )
{
if ( m_flFallVelocity > 64 & & ! g_pGameRules - > IsMultiplayer ( ) )
{
CSoundEnt : : InsertSound ( bits_SOUND_PLAYER , pev - > origin , m_flFallVelocity , 0.2 ) ;
// ALERT( at_console, "fall %f\n", m_flFallVelocity );
}
m_flFallVelocity = 0 ;
}
// select the proper animation for the player character
if ( IsAlive ( ) )
{
if ( ! pev - > velocity . x & & ! pev - > velocity . y )
SetAnimation ( PLAYER_IDLE ) ;
else if ( ( pev - > velocity . x | | pev - > velocity . y ) & & ( FBitSet ( pev - > flags , FL_ONGROUND ) ) )
SetAnimation ( PLAYER_WALK ) ;
else if ( pev - > waterlevel > 1 )
SetAnimation ( PLAYER_WALK ) ;
}
StudioFrameAdvance ( ) ;
CheckPowerups ( pev ) ;
UpdatePlayerSound ( ) ;
// Track button info so we can detect 'pressed' and 'released' buttons next frame
m_afButtonLast = pev - > button ;
pt_end :
// Decay timers on weapons
// go through all of the weapons and make a list of the ones to pack
for ( int i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
if ( m_rgpPlayerItems [ i ] )
{
CBasePlayerItem * pPlayerItem = m_rgpPlayerItems [ i ] ;
while ( pPlayerItem )
{
CBasePlayerWeapon * gun ;
gun = ( CBasePlayerWeapon * ) pPlayerItem - > GetWeaponPtr ( ) ;
if ( gun & & gun - > UseDecrement ( ) )
{
gun - > m_flNextPrimaryAttack = max ( gun - > m_flNextPrimaryAttack - gpGlobals - > frametime , - 1.0f ) ;
gun - > m_flNextSecondaryAttack = max ( gun - > m_flNextSecondaryAttack - gpGlobals - > frametime , - 0.001f ) ;
if ( gun - > m_flTimeWeaponIdle ! = 1000 )
{
gun - > m_flTimeWeaponIdle = max ( gun - > m_flTimeWeaponIdle - gpGlobals - > frametime , - 0.001f ) ;
}
if ( gun - > pev - > fuser1 ! = 1000 )
{
gun - > pev - > fuser1 = max ( gun - > pev - > fuser1 - gpGlobals - > frametime , - 0.001f ) ;
}
// Only decrement if not flagged as NO_DECREMENT
// if ( gun->m_flPumpTime != 1000 )
// {
// gun->m_flPumpTime = max( gun->m_flPumpTime - gpGlobals->frametime, -0.001f );
// }
}
pPlayerItem = pPlayerItem - > m_pNext ;
}
}
}
m_flNextAttack - = gpGlobals - > frametime ;
if ( m_flNextAttack < - 0.001 )
m_flNextAttack = - 0.001 ;
if ( m_flNextAmmoBurn ! = 1000 )
{
m_flNextAmmoBurn - = gpGlobals - > frametime ;
if ( m_flNextAmmoBurn < - 0.001 )
m_flNextAmmoBurn = - 0.001 ;
}
if ( m_flAmmoStartCharge ! = 1000 )
{
m_flAmmoStartCharge - = gpGlobals - > frametime ;
if ( m_flAmmoStartCharge < - 0.001 )
m_flAmmoStartCharge = - 0.001 ;
}
}
// checks if the spot is clear of players
BOOL IsSpawnPointValid ( CBaseEntity * pPlayer , CBaseEntity * pSpot )
{
CBaseEntity * ent = NULL ;
if ( ! pSpot - > IsTriggered ( pPlayer ) )
{
return FALSE ;
}
AvHPlayer * thePlayer = dynamic_cast < AvHPlayer * > ( pPlayer ) ;
ASSERT ( thePlayer ) ;
Vector theMinSize ;
Vector theMaxSize ;
thePlayer - > GetSize ( theMinSize , theMaxSize ) ;
int theOffset = AvHMUGetOriginOffsetForUser3 ( AvHUser3 ( thePlayer - > pev - > iuser3 ) ) ;
Vector theOriginToSpawn = pSpot - > pev - > origin ;
theOriginToSpawn . z + = theOffset ;
if ( ! AvHSUGetIsEnoughRoomForHull ( theOriginToSpawn , AvHMUGetHull ( false , thePlayer - > pev - > iuser3 ) , thePlayer - > edict ( ) ) )
return FALSE ;
//while ( (ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 )) != NULL )
// Assumes UTIL_FindEntityInSphere doesn't take size of other object into account. Players should be 2*HULL0_MAXX from each other, add another for safety
while ( ( ent = UTIL_FindEntityInSphere ( ent , pSpot - > pev - > origin , 3 * HULL0_MAXX ) ) ! = NULL )
{
// if ent is a client, don't spawn on 'em (but dead players don't block)
if ( ent - > IsPlayer ( ) & & ( ent ! = pPlayer ) & & ent - > IsAlive ( ) )
return FALSE ;
}
return TRUE ;
}
BOOL CBasePlayer : : IsAlive ( ) const
{
// Take into account spectators
return ( pev - > deadflag = = DEAD_NO ) & & pev - > health > 0 ;
}
BOOL CBasePlayer : : IsAlive ( bool inIncludeSpectating ) const
{
// Take into account spectators
BOOL theIsAlive = this - > IsAlive ( ) ;
if ( inIncludeSpectating )
{
CBaseEntity * theSpectatingEntity = this - > GetSpectatingEntity ( ) ;
if ( theSpectatingEntity )
{
theIsAlive = theSpectatingEntity - > IsAlive ( ) ;
}
}
return theIsAlive ;
}
CBaseEntity * CBasePlayer : : GetSpectatingEntity ( ) const
{
CBaseEntity * theSpectatingEntity = NULL ;
if ( this - > pev & & this - > pev - > iuser1 & & this - > pev - > iuser2 )
{
int theEntityIndex = this - > pev - > iuser2 ;
theSpectatingEntity = CBaseEntity : : Instance ( g_engfuncs . pfnPEntityOfEntIndex ( theEntityIndex ) ) ;
}
return theSpectatingEntity ;
}
void CBasePlayer : : Spawn ( void )
{
pev - > classname = MAKE_STRING ( " player " ) ;
// pev->health = 100;
// pev->armorvalue = 0;
// pev->max_health = pev->health;
pev - > takedamage = DAMAGE_AIM ;
pev - > solid = SOLID_SLIDEBOX ;
pev - > movetype = MOVETYPE_WALK ;
pev - > flags & = FL_PROXY ; // keep proxy flag sey by engine
pev - > flags | = FL_CLIENT ;
pev - > air_finished = gpGlobals - > time + 12 ;
pev - > dmg = 2 ; // initial water damage
pev - > effects = 0 ;
pev - > deadflag = DEAD_NO ;
pev - > dmg_take = 0 ;
pev - > dmg_save = 0 ;
pev - > friction = 1.0 ;
pev - > gravity = 1.0 ;
m_bitsHUDDamage = - 1 ;
m_bitsDamageType = 0 ;
m_afPhysicsFlags = 0 ;
m_fLongJump = FALSE ; // no longjump module.
g_engfuncs . pfnSetPhysicsKeyValue ( edict ( ) , " slj " , " 0 " ) ;
g_engfuncs . pfnSetPhysicsKeyValue ( edict ( ) , " hl " , " 1 " ) ;
pev - > fov = m_iFOV = 0 ; // init field of view.
m_iClientFOV = - 1 ; // make sure fov reset is sent
m_flNextDecalTime = 0 ; // let this player decal as soon as he spawns.
m_flgeigerDelay = gpGlobals - > time + 2.0 ; // wait a few seconds until user-defined message registrations
// are recieved by all clients
m_flTimeStepSound = 0 ;
m_iStepLeft = 0 ;
m_flFieldOfView = 0.5 ; // some monsters use this to determine whether or not the player is looking at them.
m_bloodColor = BLOOD_COLOR_RED ;
m_flNextAttack = UTIL_WeaponTimeBase ( ) ;
StartSneaking ( ) ;
// m_iFlashBattery = 99;
// m_flFlashLightTime = 1; // force first message
// dont let uninitialized value here hurt the player
m_flFallVelocity = 0 ;
GetGameRules ( ) - > SetDefaultPlayerTeam ( this ) ;
edict_t * theSpawnPoint = GetGameRules ( ) - > SelectSpawnPoint ( this ) ;
ASSERT ( theSpawnPoint ) ;
this - > InitPlayerFromSpawn ( theSpawnPoint ) ;
//AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(this);
//char* thePlayerModel = thePlayer->GetPlayerModel();
//SET_MODEL(ENT(pev), thePlayerModel);
SET_MODEL ( ENT ( pev ) , " models/player.mdl " ) ;
g_ulModelIndexPlayer = pev - > modelindex ;
//pev->sequence = LookupActivity( ACT_IDLE );
pev - > sequence = LookupSequence ( " idle1 " ) ;
if ( FBitSet ( pev - > flags , FL_DUCKING ) )
UTIL_SetSize ( pev , VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX ) ;
else
UTIL_SetSize ( pev , VEC_HULL_MIN , VEC_HULL_MAX ) ;
pev - > view_ofs = VEC_VIEW ;
Precache ( ) ;
m_HackedGunPos = Vector ( 0 , 32 , 0 ) ;
if ( m_iPlayerSound = = SOUNDLIST_EMPTY )
{
ALERT ( at_console , " Couldn't alloc player sound slot! \n " ) ;
}
m_fNoPlayerSound = FALSE ; // normal sound behavior.
m_pLastItem = NULL ;
m_pLastItemName = 0 ; // Added by mmcguire.
m_fInitHUD = TRUE ;
m_iClientHideHUD = - 1 ; // force this to be recalculated
m_fWeapon = FALSE ;
m_pClientActiveItem = NULL ;
m_iClientBattery = - 1 ;
// reset all ammo values to 0
for ( int i = 0 ; i < MAX_AMMO_SLOTS ; i + + )
{
m_rgAmmo [ i ] = 0 ;
m_rgAmmoLast [ i ] = 0 ; // client ammo values also have to be reset (the death hud clear messages does on the client side)
}
m_lastx = m_lasty = 0 ;
m_flNextChatTime = gpGlobals - > time ;
g_pGameRules - > PlayerSpawn ( this ) ;
}
void CBasePlayer : : Precache ( void )
{
// in the event that the player JUST spawned, and the level node graph
// was loaded, fix all of the node graph pointers before the game starts.
// !!!BUGBUG - now that we have multiplayer, this needs to be moved!
if ( WorldGraph . m_fGraphPresent & & ! WorldGraph . m_fGraphPointersSet )
{
if ( ! WorldGraph . FSetGraphPointers ( ) )
{
ALERT ( at_console , " **Graph pointers were not set! \n " ) ;
}
else
{
ALERT ( at_console , " **Graph Pointers Set! \n " ) ;
}
}
// SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific)
// because they need to precache before any clients have connected
Net_InitializeMessages ( ) ;
// init geiger counter vars during spawn and each time
// we cross a level transition
m_flgeigerRange = 1000 ;
m_igeigerRangePrev = 1000 ;
m_bitsDamageType = 0 ;
m_bitsHUDDamage = - 1 ;
m_iClientBattery = - 1 ;
m_iTrain = TRAIN_NEW ;
m_iUpdateTime = 5 ; // won't update for 1/2 a second
if ( gInitHUD )
m_fInitHUD = TRUE ;
}
int CBasePlayer : : Save ( CSave & save )
{
if ( ! CBaseMonster : : Save ( save ) )
return 0 ;
return save . WriteFields ( " PLAYER " , this , m_playerSaveData , ARRAYSIZE ( m_playerSaveData ) ) ;
}
//
// Marks everything as new so the player will resend this to the hud.
//
void CBasePlayer : : RenewItems ( void )
{
}
int CBasePlayer : : Restore ( CRestore & restore )
{
if ( ! CBaseMonster : : Restore ( restore ) )
return 0 ;
int status = restore . ReadFields ( " PLAYER " , this , m_playerSaveData , ARRAYSIZE ( m_playerSaveData ) ) ;
SAVERESTOREDATA * pSaveData = ( SAVERESTOREDATA * ) gpGlobals - > pSaveData ;
// landmark isn't present.
if ( ! pSaveData - > fUseLandmark )
{
ALERT ( at_console , " No Landmark:%s \n " , pSaveData - > szLandmarkName ) ;
// default to normal spawn
//edict_t* pentSpawnSpot = EntSelectSpawnPoint( this );
ASSERT ( FALSE ) ;
//pev->origin = VARS(pentSpawnSpot)->origin + Vector(0,0,1);
//pev->angles = VARS(pentSpawnSpot)->angles;
}
pev - > v_angle . z = 0 ; // Clear out roll
pev - > angles = pev - > v_angle ;
pev - > fixangle = TRUE ; // turn this way immediately
// Copied from spawn() for now
m_bloodColor = BLOOD_COLOR_RED ;
g_ulModelIndexPlayer = pev - > modelindex ;
if ( FBitSet ( pev - > flags , FL_DUCKING ) )
{
// Use the crouch HACK
//FixPlayerCrouchStuck( edict() );
// Don't need to do this with new player prediction code.
UTIL_SetSize ( pev , VEC_DUCK_HULL_MIN , VEC_DUCK_HULL_MAX ) ;
}
else
{
UTIL_SetSize ( pev , VEC_HULL_MIN , VEC_HULL_MAX ) ;
}
g_engfuncs . pfnSetPhysicsKeyValue ( edict ( ) , " hl " , " 1 " ) ;
if ( m_fLongJump )
{
g_engfuncs . pfnSetPhysicsKeyValue ( edict ( ) , " slj " , " 1 " ) ;
}
else
{
g_engfuncs . pfnSetPhysicsKeyValue ( edict ( ) , " slj " , " 0 " ) ;
}
RenewItems ( ) ;
// HACK: This variable is saved/restored in CBaseMonster as a time variable, but we're using it
// as just a counter. Ideally, this needs its own variable that's saved as a plain float.
// Barring that, we clear it out here instead of using the incorrect restored time value.
m_flNextAttack = UTIL_WeaponTimeBase ( ) ;
return status ;
}
void CBasePlayer : : SelectNextItem ( int iItem )
{
CBasePlayerItem * pItem ;
pItem = m_rgpPlayerItems [ iItem ] ;
if ( ! pItem )
return ;
if ( pItem = = m_pActiveItem )
{
// select the next one in the chain
pItem = m_pActiveItem - > m_pNext ;
if ( ! pItem )
{
return ;
}
CBasePlayerItem * pLast ;
pLast = pItem ;
while ( pLast - > m_pNext )
pLast = pLast - > m_pNext ;
// relink chain
pLast - > m_pNext = m_pActiveItem ;
m_pActiveItem - > m_pNext = NULL ;
m_rgpPlayerItems [ iItem ] = pItem ;
}
ResetAutoaim ( ) ;
// FIX, this needs to queue them up and delay
if ( m_pActiveItem )
{
m_pActiveItem - > Holster ( ) ;
}
m_pActiveItem = pItem ;
if ( m_pActiveItem )
{
m_pActiveItem - > Deploy ( ) ;
m_pActiveItem - > UpdateItemInfo ( ) ;
}
}
void CBasePlayer : : SelectItem ( const char * pstr )
{
if ( ! pstr )
return ;
CBasePlayerItem * pItem = NULL ;
for ( int i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
if ( m_rgpPlayerItems [ i ] )
{
pItem = m_rgpPlayerItems [ i ] ;
while ( pItem )
{
if ( FClassnameIs ( pItem - > pev , pstr ) )
break ;
pItem = pItem - > m_pNext ;
}
}
if ( pItem )
break ;
}
if ( ! pItem )
return ;
if ( ( pItem = = m_pActiveItem ) | | ( m_pActiveItem ! = NULL & & ! m_pActiveItem - > CanHolster ( ) ) )
return ;
ResetAutoaim ( ) ;
// FIX, this needs to queue them up and delay
if ( m_pActiveItem )
m_pActiveItem - > Holster ( ) ;
m_pLastItem = m_pActiveItem ;
if ( m_pLastItem )
{
m_pLastItemName = m_pLastItem - > pev - > classname ;
}
else
{
m_pLastItemName = 0 ;
}
m_pActiveItem = pItem ;
if ( m_pActiveItem )
{
m_pActiveItem - > Deploy ( ) ;
m_pActiveItem - > UpdateItemInfo ( ) ;
}
}
//note this should no longer be called, and asserts if used
void CBasePlayer : : SelectLastItem ( void )
{
if ( ! m_pLastItem )
{
// Try the last item name.
if ( m_pLastItemName ! = 0 )
{
for ( int i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
CBasePlayerItem * theCurrentItem = this - > m_rgpPlayerItems [ i ] ;
while ( theCurrentItem )
{
if ( FClassnameIs ( theCurrentItem - > pev , STRING ( m_pLastItemName ) ) )
{
m_pLastItem = theCurrentItem ;
break ;
}
theCurrentItem = theCurrentItem - > m_pNext ;
}
}
}
}
if ( ! m_pLastItem )
{
return ;
}
this - > SelectItem ( STRING ( m_pLastItem - > pev - > classname ) ) ; //replaced copy-and-paste from SelectItem with actual SelectItem call
}
BOOL CBasePlayer : : HasItem ( CBasePlayerItem * inWeapon )
{
// Return true if we have a weapon of this type
bool theHasWeapon = false ;
for ( int i = 0 ; ( i < MAX_ITEM_TYPES ) & & ! theHasWeapon ; i + + )
{
CBasePlayerItem * theCurrentItem = this - > m_rgpPlayerItems [ i ] ;
while ( theCurrentItem & & ! theHasWeapon )
{
if ( FClassnameIs ( theCurrentItem - > pev , STRING ( inWeapon - > pev - > classname ) ) )
{
theHasWeapon = true ;
}
theCurrentItem = theCurrentItem - > m_pNext ;
}
}
return theHasWeapon ;
}
BOOL CBasePlayer : : HasItemWithFlag ( int inFlag , CBasePlayerItem * & outItem )
{
// Return true if we have a weapon of this type
bool theHasWeaponWithFlag = false ;
for ( int i = 0 ; ( i < MAX_ITEM_TYPES ) & & ! theHasWeaponWithFlag ; i + + )
{
CBasePlayerItem * theCurrentItem = this - > m_rgpPlayerItems [ i ] ;
while ( theCurrentItem & & ! theHasWeaponWithFlag )
{
ItemInfo theItemInfo ;
theCurrentItem - > GetItemInfo ( & theItemInfo ) ;
int theFlags = theItemInfo . iFlags ;
if ( theFlags & inFlag )
{
theHasWeaponWithFlag = true ;
outItem = theCurrentItem ;
}
theCurrentItem = theCurrentItem - > m_pNext ;
}
}
return theHasWeaponWithFlag ;
}
//==============================================
// HasWeapons - do I have any weapons at all?
//==============================================
BOOL CBasePlayer : : HasWeapons ( void )
{
int i ;
for ( i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
if ( m_rgpPlayerItems [ i ] )
{
return TRUE ;
}
}
return FALSE ;
}
void CBasePlayer : : SelectPrevItem ( int iItem )
{
}
char * CBasePlayer : : TeamID ( void )
{
if ( pev = = NULL ) // Not fully connected yet
return " " ;
// return their team name
return m_szTeamName ;
}
void CBasePlayer : : SetTeamID ( const char * inTeamID )
{
strncpy ( this - > m_szTeamName , inTeamID , TEAM_NAME_LENGTH ) ;
}
//==============================================
// !!!UNDONE:ultra temporary SprayCan entity to apply
// decal frame at a time. For PreAlpha CD
//==============================================
class CSprayCan : public CBaseEntity
{
public :
void Spawn ( entvars_t * pevOwner ) ;
void Think ( void ) ;
virtual int ObjectCaps ( void ) { return FCAP_DONT_SAVE ; }
} ;
void CSprayCan : : Spawn ( entvars_t * pevOwner )
{
pev - > origin = pevOwner - > origin + Vector ( 0 , 0 , 32 ) ;
pev - > angles = pevOwner - > v_angle ;
pev - > owner = ENT ( pevOwner ) ;
pev - > frame = 0 ;
pev - > nextthink = gpGlobals - > time + 0.1 ;
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/sprayer.wav " , 1 , ATTN_NORM ) ;
}
void CSprayCan : : Think ( void )
{
TraceResult tr ;
int playernum ;
int nFrames ;
CBasePlayer * pPlayer ;
pPlayer = ( CBasePlayer * ) GET_PRIVATE ( pev - > owner ) ;
if ( pPlayer )
nFrames = pPlayer - > GetCustomDecalFrames ( ) ;
else
nFrames = - 1 ;
playernum = ENTINDEX ( pev - > owner ) ;
// ALERT(at_console, "Spray by player %i, %i of %i\n", playernum, (int)(pev->frame + 1), nFrames);
UTIL_MakeVectors ( pev - > angles ) ;
UTIL_TraceLine ( pev - > origin , pev - > origin + gpGlobals - > v_forward * 128 , ignore_monsters , pev - > owner , & tr ) ;
// No customization present.
if ( nFrames = = - 1 )
{
UTIL_DecalTrace ( & tr , DECAL_LAMBDA6 ) ;
UTIL_Remove ( this ) ;
}
else
{
UTIL_PlayerDecalTrace ( & tr , playernum , pev - > frame , TRUE ) ;
// Just painted last custom frame.
if ( pev - > frame + + > = ( nFrames - 1 ) )
UTIL_Remove ( this ) ;
}
pev - > nextthink = gpGlobals - > time + 0.1 ;
}
class CBloodSplat : public CBaseEntity
{
public :
void Spawn ( entvars_t * pevOwner ) ;
void Spray ( void ) ;
} ;
void CBloodSplat : : Spawn ( entvars_t * pevOwner )
{
pev - > origin = pevOwner - > origin + Vector ( 0 , 0 , 32 ) ;
pev - > angles = pevOwner - > v_angle ;
pev - > owner = ENT ( pevOwner ) ;
SetThink ( & CBloodSplat : : Spray ) ;
pev - > nextthink = gpGlobals - > time + 0.1 ;
}
void CBloodSplat : : Spray ( void )
{
TraceResult tr ;
if ( g_Language ! = LANGUAGE_GERMAN )
{
UTIL_MakeVectors ( pev - > angles ) ;
UTIL_TraceLine ( pev - > origin , pev - > origin + gpGlobals - > v_forward * 128 , ignore_monsters , pev - > owner , & tr ) ;
UTIL_BloodDecalTrace ( & tr , BLOOD_COLOR_RED ) ;
}
SetThink ( & CBloodSplat : : SUB_Remove ) ;
pev - > nextthink = gpGlobals - > time + 0.1 ;
}
//==============================================
void CBasePlayer : : GiveNamedItem ( const char * pszName , bool inSendMessage )
{
edict_t * pent ;
int istr = MAKE_STRING ( pszName ) ;
pent = CREATE_NAMED_ENTITY ( istr ) ;
if ( FNullEnt ( pent ) )
{
ALERT ( at_console , " NULL Ent in GiveNamedItem! \n " ) ;
return ;
}
VARS ( pent ) - > origin = pev - > origin ;
pent - > v . spawnflags | = SF_NORESPAWN ;
DispatchSpawn ( pent ) ;
DispatchTouch ( pent , ENT ( pev ) ) ;
if ( inSendMessage )
{
CBaseEntity * theEntity = CBaseEntity : : Instance ( ENT ( pent ) ) ;
ASSERT ( theEntity ) ;
CBasePlayerWeapon * theWeapon = dynamic_cast < CBasePlayerWeapon * > ( theEntity ) ;
//ASSERT(theWeapon);
if ( theWeapon )
{
int theWeaponID = theWeapon - > m_iId ;
NetMsg_WeapPickup ( pev , theWeaponID ) ;
}
}
}
CBaseEntity * FindEntityForward ( CBaseEntity * pMe )
{
TraceResult tr ;
UTIL_MakeVectors ( pMe - > pev - > v_angle ) ;
UTIL_TraceLine ( pMe - > pev - > origin + pMe - > pev - > view_ofs , pMe - > pev - > origin + pMe - > pev - > view_ofs + gpGlobals - > v_forward * 8192 , dont_ignore_monsters , pMe - > edict ( ) , & tr ) ;
if ( tr . flFraction ! = 1.0 & & ! FNullEnt ( tr . pHit ) )
{
CBaseEntity * pHit = CBaseEntity : : Instance ( tr . pHit ) ;
return pHit ;
}
return NULL ;
}
BOOL CBasePlayer : : FlashlightIsOn ( void )
{
return FBitSet ( pev - > effects , EF_DIMLIGHT ) ;
}
const float kFlashlightVolume = .3f ;
void CBasePlayer : : FlashlightTurnOn ( void )
{
if ( ! g_pGameRules - > FAllowFlashlight ( ) )
{
return ;
}
if ( ( pev - > weapons & ( 1 < < WEAPON_SUIT ) ) )
{
EMIT_SOUND_DYN ( ENT ( pev ) , CHAN_WEAPON , SOUND_FLASHLIGHT_ON , kFlashlightVolume , ATTN_NORM , 0 , PITCH_NORM ) ;
SetBits ( pev - > effects , EF_DIMLIGHT ) ;
/*MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev );
WRITE_BYTE ( 1 ) ;
WRITE_BYTE ( m_iFlashBattery ) ;
MESSAGE_END ( ) ;
m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals - > time ; */
}
}
void CBasePlayer : : FlashlightTurnOff ( void )
{
EMIT_SOUND_DYN ( ENT ( pev ) , CHAN_WEAPON , SOUND_FLASHLIGHT_OFF , kFlashlightVolume , ATTN_NORM , 0 , PITCH_NORM ) ;
ClearBits ( pev - > effects , EF_DIMLIGHT ) ;
/*MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev );
WRITE_BYTE ( 0 ) ;
WRITE_BYTE ( m_iFlashBattery ) ;
MESSAGE_END ( ) ;
m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals - > time ; */
}
/*
= = = = = = = = = = = = = = =
ForceClientDllUpdate
When recording a demo , we need to have the server tell us the entire client state
so that the client side . dll can behave correctly .
Reset stuff so that the state is transmitted .
= = = = = = = = = = = = = = =
*/
void CBasePlayer : : ForceClientDllUpdate ( void )
{
m_iClientHealth = - 1 ;
m_iClientBattery = - 1 ;
m_iTrain | = TRAIN_NEW ; // Force new train message.
m_fWeapon = FALSE ; // Force weapon send
m_fKnownItem = FALSE ; // Force weaponinit messages.
m_fInitHUD = TRUE ; // Force HUD gmsgResetHUD message
// Now force all the necessary messages
// to be sent.
UpdateClientData ( ) ;
// m_fWeapon = TRUE; // Force weapon send
}
/*
= = = = = = = = = = = =
ImpulseCommands
= = = = = = = = = = = =
*/
extern float g_flWeaponCheat ;
void CBasePlayer : : ImpulseCommands ( )
{
TraceResult tr ; // UNDONE: kill me! This is temporary for PreAlpha CDs
int iImpulse = ( int ) pev - > impulse ;
switch ( iImpulse )
{
case IMPULSE_FLASHLIGHT :
// temporary flashlight for level designers
if ( FlashlightIsOn ( ) )
{
FlashlightTurnOff ( ) ;
}
else
{
FlashlightTurnOn ( ) ;
}
break ;
case IMPULSE_SPRAYPAINT : // paint decal
if ( gpGlobals - > time < m_flNextDecalTime )
{
// too early!
break ;
}
UTIL_MakeVectors ( pev - > v_angle ) ;
UTIL_TraceLine ( pev - > origin + pev - > view_ofs , pev - > origin + pev - > view_ofs + gpGlobals - > v_forward * 128 , ignore_monsters , ENT ( pev ) , & tr ) ;
if ( tr . flFraction ! = 1.0 )
{ // line hit something, so paint a decal
m_flNextDecalTime = gpGlobals - > time + decalfrequency . value ;
CSprayCan * pCan = GetClassPtr ( ( CSprayCan * ) NULL ) ;
pCan - > Spawn ( pev ) ;
}
break ;
// HLDSDK 2.3 update
// case IMPULSE_DEMORECORD: // Demo recording, update client dll specific data again.
// ForceClientDllUpdate();
// break;
default :
// check all of the cheat impulse commands now
CheatImpulseCommands ( iImpulse ) ;
break ;
}
pev - > impulse = 0 ;
}
//=========================================================
//=========================================================
void CBasePlayer : : CheatImpulseCommands ( int iImpulse )
{
}
//
// Add a weapon to the player (Item == Weapon == Selectable Object)
//
int CBasePlayer : : AddPlayerItem ( CBasePlayerItem * pItem )
{
CBasePlayerItem * pInsert ;
pInsert = m_rgpPlayerItems [ pItem - > iItemSlot ( ) ] ;
ItemInfo ii ;
pItem - > GetItemInfo ( & ii ) ;
if ( pItem - > iItemSlot ( ) = = 1 | | ii . iId = = AVH_WEAPON_MINE | | ii . iId = = AVH_WEAPON_WELDER ) {
this - > EffectivePlayerClassChanged ( ) ;
}
while ( pInsert )
{
if ( FClassnameIs ( pInsert - > pev , STRING ( pItem - > pev - > classname ) ) )
{
if ( pItem - > AddDuplicate ( pInsert ) )
{
g_pGameRules - > PlayerGotWeapon ( this , pItem ) ;
pItem - > CheckRespawn ( ) ;
// ugly hack to update clip w/o an update clip message
pInsert - > UpdateItemInfo ( ) ;
if ( m_pActiveItem )
m_pActiveItem - > UpdateItemInfo ( ) ;
pItem - > Kill ( ) ;
}
else if ( gEvilImpulse101 )
{
// FIXME: remove anyway for deathmatch testing
pItem - > Kill ( ) ;
}
return FALSE ;
}
pInsert = pInsert - > m_pNext ;
}
if ( pItem - > AddToPlayer ( this ) )
{
g_pGameRules - > PlayerGotWeapon ( this , pItem ) ;
pItem - > CheckRespawn ( ) ;
pItem - > m_pNext = m_rgpPlayerItems [ pItem - > iItemSlot ( ) ] ;
m_rgpPlayerItems [ pItem - > iItemSlot ( ) ] = pItem ;
// should we switch to this item?
if ( g_pGameRules - > FShouldSwitchWeapon ( this , pItem ) )
{
SwitchWeapon ( pItem ) ;
}
return TRUE ;
}
else if ( gEvilImpulse101 )
{
// FIXME: remove anyway for deathmatch testing
pItem - > Kill ( ) ;
}
return FALSE ;
}
int CBasePlayer : : RemovePlayerItem ( CBasePlayerItem * pItem )
{
if ( m_pActiveItem = = pItem )
{
ResetAutoaim ( ) ;
pItem - > Holster ( ) ;
pItem - > pev - > nextthink = 0 ; // crowbar may be trying to swing again, etc.
pItem - > SetThink ( NULL ) ;
m_pActiveItem = NULL ;
pev - > viewmodel = 0 ;
pev - > weaponmodel = 0 ;
}
else if ( m_pLastItem = = pItem )
m_pLastItem = NULL ;
AvHBasePlayerWeapon * theWeapon = dynamic_cast < AvHBasePlayerWeapon * > ( pItem ) ;
if ( theWeapon )
{
ItemInfo theInfo ;
theWeapon - > GetItemInfo ( & theInfo ) ;
int theID = theInfo . iId ;
this - > pev - > weapons & = ~ ( 1 < < theID ) ;
}
CBasePlayerItem * pPrev = m_rgpPlayerItems [ pItem - > iItemSlot ( ) ] ;
if ( pPrev = = pItem )
{
m_rgpPlayerItems [ pItem - > iItemSlot ( ) ] = pItem - > m_pNext ;
return TRUE ;
}
else
{
while ( pPrev & & pPrev - > m_pNext ! = pItem )
{
pPrev = pPrev - > m_pNext ;
}
if ( pPrev )
{
pPrev - > m_pNext = pItem - > m_pNext ;
return TRUE ;
}
}
return FALSE ;
}
//
// Returns the unique ID for the ammo, or -1 if error
//
int CBasePlayer : : GiveAmmo ( int iCount , char * szName , int iMax )
{
if ( ! szName )
{
// no ammo.
return - 1 ;
}
if ( ! g_pGameRules - > CanHaveAmmo ( this , szName , iMax ) )
{
// game rules say I can't have any more of this ammo type.
return - 1 ;
}
int i = 0 ;
i = GetAmmoIndex ( szName ) ;
if ( i < 0 | | i > = MAX_AMMO_SLOTS )
return - 1 ;
int iAdd = min ( iCount , iMax - m_rgAmmo [ i ] ) ;
if ( iAdd < 1 )
return i ;
m_rgAmmo [ i ] + = iAdd ;
// Send the message that ammo has been picked up
NetMsg_AmmoPickup ( pev , GetAmmoIndex ( szName ) , iAdd ) ;
TabulateAmmo ( ) ;
return i ;
}
/*
= = = = = = = = = = = =
ItemPreFrame
Called every frame by the player PreThink
= = = = = = = = = = = =
*/
void CBasePlayer : : ItemPreFrame ( )
{
if ( m_flNextAttack > 0 )
{
return ;
}
if ( ! m_pActiveItem )
return ;
m_pActiveItem - > ItemPreFrame ( ) ;
}
/*
= = = = = = = = = = = =
ItemPostFrame
Called every frame by the player PostThink
= = = = = = = = = = = =
*/
void CBasePlayer : : ItemPostFrame ( )
{
static int fInSelect = FALSE ;
// check if the player is using a tank
if ( m_pTank ! = NULL )
return ;
ImpulseCommands ( ) ;
if ( m_flNextAttack > 0 )
{
return ;
}
if ( ! m_pActiveItem )
return ;
// Only update weapon once game has started (no firing before then)
//if(GetGameRules()->GetGameStarted())
//{
this - > m_pActiveItem - > ItemPostFrame ( ) ;
//}
}
int CBasePlayer : : AmmoInventory ( int iAmmoIndex )
{
if ( iAmmoIndex = = - 1 )
{
return - 1 ;
}
return m_rgAmmo [ iAmmoIndex ] ;
}
int CBasePlayer : : GetAmmoIndex ( const char * psz )
{
int i ;
if ( ! psz )
return - 1 ;
for ( i = 1 ; i < MAX_AMMO_SLOTS ; i + + )
{
if ( ! CBasePlayerItem : : AmmoInfoArray [ i ] . pszName )
continue ;
if ( stricmp ( psz , CBasePlayerItem : : AmmoInfoArray [ i ] . pszName ) = = 0 )
return i ;
}
return - 1 ;
}
int CBasePlayer : : GetMaxWalkSpeed ( void ) const
{
return 220 ;
}
// Called from UpdateClientData
// makes sure the client has all the necessary ammo info, if values have changed
void CBasePlayer : : SendAmmoUpdate ( void )
{
int theAmmoToSend [ MAX_AMMO_SLOTS ] ;
memcpy ( & theAmmoToSend , & this - > m_rgAmmo , MAX_AMMO_SLOTS * sizeof ( int ) ) ;
// if(this->pev->iuser1 == OBS_IN_EYE)
// {
// CBasePlayer* theEntity = NULL;
// if(AvHSUGetEntityFromIndex(this->pev->iuser2, theEntity))
// {
// memcpy(&theAmmoToSend, &theEntity->m_rgAmmo, MAX_AMMO_SLOTS*sizeof(int));
// }
// }
for ( int i = 0 ; i < MAX_AMMO_SLOTS ; i + + )
{
if ( theAmmoToSend [ i ] ! = m_rgAmmoLast [ i ] )
{
m_rgAmmoLast [ i ] = theAmmoToSend [ i ] ;
// TODO: Fix me!
//ASSERT( m_rgAmmo[i] >= 0 );
//ASSERT( m_rgAmmo[i] < 255 );
if ( ( theAmmoToSend [ i ] > = 0 ) & & ( theAmmoToSend [ i ] < 255 ) )
{
// send "Ammo" update message
NetMsg_AmmoX ( pev , i , max ( min ( theAmmoToSend [ i ] , 254 ) , 0 ) ) ;
}
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
UpdateClientData
resends any changed player HUD info to the client .
Called every frame by PlayerPreThink
Also called at start of demo recording and playback by
ForceClientDllUpdate to ensure the demo gets messages
reflecting all of the HUD state info .
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*/
void CBasePlayer : : UpdateClientData ( void )
{
CBasePlayer * thePlayerToUseForWeaponUpdates = this ;
// if(this->pev->iuser1 == OBS_IN_EYE)
// {
// CBasePlayer* theSpectatingPlayer = NULL;
// if(AvHSUGetEntityFromIndex(this->pev->iuser2, theSpectatingPlayer))
// {
// thePlayerToUseForWeaponUpdates = theSpectatingPlayer;
// }
// }
if ( m_fInitHUD )
{
m_fInitHUD = FALSE ;
gInitHUD = FALSE ;
NetMsg_ResetHUD ( pev ) ;
if ( ! m_fGameHUDInitialized )
{
NetMsg_InitHUD ( pev ) ;
g_pGameRules - > InitHUD ( this ) ;
m_fGameHUDInitialized = TRUE ;
if ( g_pGameRules - > IsMultiplayer ( ) )
{
FireTargets ( " game_playerjoin " , this , this , USE_TOGGLE , 0 ) ;
}
}
FireTargets ( " game_playerspawn " , this , this , USE_TOGGLE , 0 ) ;
InitStatusBar ( ) ;
}
if ( m_iHideHUD ! = m_iClientHideHUD )
{
NetMsg_HideWeapon ( pev , m_iHideHUD ) ;
m_iClientHideHUD = m_iHideHUD ;
}
if ( m_iFOV ! = m_iClientFOV )
{
NetMsg_SetFOV ( pev , m_iFOV ) ;
// cache FOV change at end of function, so weapon updates can see that FOV has changed
}
// HACKHACK -- send the message to display the game title
if ( gDisplayTitle )
{
NetMsg_ShowGameTitle ( pev ) ;
gDisplayTitle = 0 ;
}
if ( ( int ) pev - > health ! = m_iClientHealth ) //: this cast to int is important, otherwise we spam the message, this is just a quick and easy fix.
{
NetMsg_Health ( pev , max ( pev - > health , 0.0f ) ) ;
m_iClientHealth = ( int ) pev - > health ;
}
if ( ( int ) pev - > armorvalue ! = m_iClientBattery )
{
NetMsg_Battery ( pev , ( int ) pev - > armorvalue ) ;
m_iClientBattery = ( int ) pev - > armorvalue ;
}
if ( pev - > dmg_take | | pev - > dmg_save | | m_bitsHUDDamage ! = m_bitsDamageType )
{
// Comes from inside me if not set
Vector damageOrigin = pev - > origin ;
// send "damage" message
// causes screen to flash, and pain compass to show direction of damage
edict_t * other = pev - > dmg_inflictor ;
if ( other )
{
CBaseEntity * pEntity = CBaseEntity : : Instance ( other ) ;
if ( pEntity )
damageOrigin = pEntity - > Center ( ) ;
}
// only send down damage type that have hud art
int visibleDamageBits = m_bitsDamageType & DMG_SHOWNHUD ;
float origin [ ] = { damageOrigin . x , damageOrigin . y , damageOrigin . z } ;
NetMsg_Damage ( pev , pev - > dmg_save , pev - > dmg_take , 0 , origin ) ;
pev - > dmg_take = 0 ;
pev - > dmg_save = 0 ;
m_bitsHUDDamage = m_bitsDamageType ;
// Clear off non-time-based damage indicators
m_bitsDamageType & = DMG_TIMEBASED ;
}
if ( m_iTrain & TRAIN_NEW )
{
// send "health" update message
NetMsg_Train ( pev , m_iTrain & 0xF ) ;
m_iTrain & = ~ TRAIN_NEW ;
}
//
// New Weapon?
//
bool forceCurWeaponUpdate = false ;
if ( ! m_fKnownItem )
{
m_fKnownItem = TRUE ;
// WeaponInit Message
// byte = # of weapons
//
// for each weapon:
// byte name str length (not including null)
// bytes... name
// byte Ammo Type
// byte Ammo2 Type
// byte bucket
// byte bucket pos
// byte flags
// ???? Icons
// Send ALL the weapon info now
this - > SendWeaponUpdate ( ) ;
// : HACK force an full curweapon update after each bunch of weaponlists sent
forceCurWeaponUpdate = true ;
// :
}
SendAmmoUpdate ( ) ;
// Update all the items
for ( int i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
if ( forceCurWeaponUpdate = = true )
m_fWeapon = FALSE ;
if ( thePlayerToUseForWeaponUpdates - > m_rgpPlayerItems [ i ] ) // each item updates it's successors
thePlayerToUseForWeaponUpdates - > m_rgpPlayerItems [ i ] - > UpdateClientData ( thePlayerToUseForWeaponUpdates ) ;
}
if ( forceCurWeaponUpdate = = true )
m_fWeapon = TRUE ;
// Cache and client weapon change
m_pClientActiveItem = thePlayerToUseForWeaponUpdates - > m_pActiveItem ;
m_iClientFOV = thePlayerToUseForWeaponUpdates - > m_iFOV ;
// Update Status Bar if we're playing
AvHPlayer * thePlayer = dynamic_cast < AvHPlayer * > ( this ) ;
if ( thePlayer & & ( thePlayer - > GetPlayMode ( ) = = PLAYMODE_PLAYING ) )
{
if ( m_flNextSBarUpdateTime < gpGlobals - > time )
{
UpdateStatusBar ( ) ;
m_flNextSBarUpdateTime = gpGlobals - > time + 0.2 ;
}
}
}
void CBasePlayer : : SendWeaponUpdate ( )
{
int i ;
for ( i = 0 ; i < MAX_WEAPONS ; i + + )
{
ItemInfo & II = CBasePlayerItem : : ItemInfoArray [ i ] ;
if ( ! II . iId )
continue ;
const char * pszName ;
if ( ! II . pszName )
pszName = " Empty " ;
else
pszName = II . pszName ;
WeaponList weapon ;
weapon . weapon_name = pszName ;
weapon . ammo1_type = GetAmmoIndex ( II . pszAmmo1 ) ;
weapon . ammo2_type = GetAmmoIndex ( II . pszAmmo2 ) ;
weapon . ammo1_max_amnt = II . iMaxAmmo1 ;
weapon . ammo2_max_amnt = II . iMaxAmmo2 ;
weapon . bucket = II . iSlot ;
weapon . bucket_pos = II . iPosition ;
weapon . bit_index = II . iId ;
weapon . flags = II . iFlags ;
NetMsg_WeaponList ( pev , weapon ) ;
}
}
//=========================================================
// FBecomeProne - Overridden for the player to set the proper
// physics flags when a barnacle grabs player.
//=========================================================
BOOL CBasePlayer : : FBecomeProne ( void )
{
m_afPhysicsFlags | = PFLAG_ONBARNACLE ;
return TRUE ;
}
//=========================================================
// BarnacleVictimBitten - bad name for a function that is called
// by Barnacle victims when the barnacle pulls their head
// into its mouth. For the player, just die.
//=========================================================
void CBasePlayer : : BarnacleVictimBitten ( entvars_t * pevBarnacle )
{
TakeDamage ( pevBarnacle , pevBarnacle , pev - > health + pev - > armorvalue , DMG_SLASH | DMG_ALWAYSGIB ) ;
}
//=========================================================
// BarnacleVictimReleased - overridden for player who has
// physics flags concerns.
//=========================================================
void CBasePlayer : : BarnacleVictimReleased ( void )
{
m_afPhysicsFlags & = ~ PFLAG_ONBARNACLE ;
}
//=========================================================
// Illumination
// return player light level plus virtual muzzle flash
//=========================================================
int CBasePlayer : : Illumination ( void )
{
int iIllum = CBaseEntity : : Illumination ( ) ;
iIllum + = m_iWeaponFlash ;
if ( iIllum > 255 )
return 255 ;
return iIllum ;
}
void CBasePlayer : : EnableControl ( BOOL fControl )
{
if ( ! fControl )
pev - > flags | = FL_FROZEN ;
else
pev - > flags & = ~ FL_FROZEN ;
}
# define DOT_1DEGREE 0.9998476951564
# define DOT_2DEGREE 0.9993908270191
# define DOT_3DEGREE 0.9986295347546
# define DOT_4DEGREE 0.9975640502598
# define DOT_5DEGREE 0.9961946980917
# define DOT_6DEGREE 0.9945218953683
# define DOT_7DEGREE 0.9925461516413
# define DOT_8DEGREE 0.9902680687416
# define DOT_9DEGREE 0.9876883405951
# define DOT_10DEGREE 0.9848077530122
# define DOT_15DEGREE 0.9659258262891
# define DOT_20DEGREE 0.9396926207859
# define DOT_25DEGREE 0.9063077870367
//=========================================================
// Autoaim
// set crosshair position to point to enemey
//=========================================================
Vector CBasePlayer : : GetAutoaimVector ( float flDelta )
{
if ( g_iSkillLevel = = SKILL_HARD )
{
UTIL_MakeVectors ( pev - > v_angle + pev - > punchangle ) ;
return gpGlobals - > v_forward ;
}
Vector vecSrc = GetGunPosition ( ) ;
float flDist = 8192 ;
// always use non-sticky autoaim
// UNDONE: use sever variable to chose!
if ( 1 | | g_iSkillLevel = = SKILL_MEDIUM )
{
m_vecAutoAim = Vector ( 0 , 0 , 0 ) ;
// flDelta *= 0.5;
}
BOOL m_fOldTargeting = m_fOnTarget ;
Vector angles = AutoaimDeflection ( vecSrc , flDist , flDelta ) ;
// update ontarget if changed
if ( ! g_pGameRules - > AllowAutoTargetCrosshair ( ) )
m_fOnTarget = 0 ;
else if ( m_fOldTargeting ! = m_fOnTarget )
{
m_pActiveItem - > UpdateItemInfo ( ) ;
}
if ( angles . x > 180 )
angles . x - = 360 ;
if ( angles . x < - 180 )
angles . x + = 360 ;
if ( angles . y > 180 )
angles . y - = 360 ;
if ( angles . y < - 180 )
angles . y + = 360 ;
if ( angles . x > 25 )
angles . x = 25 ;
if ( angles . x < - 25 )
angles . x = - 25 ;
if ( angles . y > 12 )
angles . y = 12 ;
if ( angles . y < - 12 )
angles . y = - 12 ;
// always use non-sticky autoaim
// UNDONE: use sever variable to chose!
if ( 0 | | g_iSkillLevel = = SKILL_EASY )
{
m_vecAutoAim = m_vecAutoAim * 0.67 + angles * 0.33 ;
}
else
{
m_vecAutoAim = angles * 0.9 ;
}
// m_vecAutoAim = m_vecAutoAim * 0.99;
// Don't send across network if sv_aim is 0
if ( g_psv_aim - > value ! = 0 )
{
if ( m_vecAutoAim . x ! = m_lastx | |
m_vecAutoAim . y ! = m_lasty )
{
SET_CROSSHAIRANGLE ( edict ( ) , - m_vecAutoAim . x , m_vecAutoAim . y ) ;
m_lastx = m_vecAutoAim . x ;
m_lasty = m_vecAutoAim . y ;
}
}
// ALERT( at_console, "%f %f\n", angles.x, angles.y );
UTIL_MakeVectors ( pev - > v_angle + pev - > punchangle + m_vecAutoAim ) ;
return gpGlobals - > v_forward ;
}
Vector CBasePlayer : : AutoaimDeflection ( Vector & vecSrc , float flDist , float flDelta )
{
edict_t * pEdict = g_engfuncs . pfnPEntityOfEntIndex ( 1 ) ;
CBaseEntity * pEntity ;
float bestdot ;
Vector bestdir ;
edict_t * bestent ;
TraceResult tr ;
if ( g_psv_aim - > value = = 0 )
{
m_fOnTarget = FALSE ;
return g_vecZero ;
}
UTIL_MakeVectors ( pev - > v_angle + pev - > punchangle + m_vecAutoAim ) ;
// try all possible entities
bestdir = gpGlobals - > v_forward ;
bestdot = flDelta ; // +- 10 degrees
bestent = NULL ;
m_fOnTarget = FALSE ;
UTIL_TraceLine ( vecSrc , vecSrc + bestdir * flDist , dont_ignore_monsters , edict ( ) , & tr ) ;
if ( tr . pHit & & tr . pHit - > v . takedamage ! = DAMAGE_NO )
{
// don't look through water
if ( ! ( ( pev - > waterlevel ! = 3 & & tr . pHit - > v . waterlevel = = 3 )
| | ( pev - > waterlevel = = 3 & & tr . pHit - > v . waterlevel = = 0 ) ) )
{
if ( tr . pHit - > v . takedamage = = DAMAGE_AIM )
m_fOnTarget = TRUE ;
return m_vecAutoAim ;
}
}
for ( int i = 1 ; i < gpGlobals - > maxEntities ; i + + , pEdict + + )
{
Vector center ;
Vector dir ;
float dot ;
if ( pEdict - > free ) // Not in use
continue ;
if ( pEdict - > v . takedamage ! = DAMAGE_AIM )
continue ;
if ( pEdict = = edict ( ) )
continue ;
// if (pev->team > 0 && pEdict->v.team == pev->team)
// continue; // don't aim at teammate
if ( ! g_pGameRules - > ShouldAutoAim ( this , pEdict ) )
continue ;
pEntity = Instance ( pEdict ) ;
if ( pEntity = = NULL )
continue ;
if ( ! pEntity - > IsAlive ( ) )
continue ;
// don't look through water
if ( ( pev - > waterlevel ! = 3 & & pEntity - > pev - > waterlevel = = 3 )
| | ( pev - > waterlevel = = 3 & & pEntity - > pev - > waterlevel = = 0 ) )
continue ;
center = pEntity - > BodyTarget ( vecSrc ) ;
dir = ( center - vecSrc ) . Normalize ( ) ;
// make sure it's in front of the player
if ( DotProduct ( dir , gpGlobals - > v_forward ) < 0 )
continue ;
dot = fabs ( DotProduct ( dir , gpGlobals - > v_right ) )
+ fabs ( DotProduct ( dir , gpGlobals - > v_up ) ) * 0.5 ;
// tweek for distance
dot * = 1.0 + 0.2 * ( ( center - vecSrc ) . Length ( ) / flDist ) ;
if ( dot > bestdot )
continue ; // to far to turn
UTIL_TraceLine ( vecSrc , center , dont_ignore_monsters , edict ( ) , & tr ) ;
if ( tr . flFraction ! = 1.0 & & tr . pHit ! = pEdict )
{
// ALERT( at_console, "hit %s, can't see %s\n", STRING( tr.pHit->v.classname ), STRING( pEdict->v.classname ) );
continue ;
}
// don't shoot at friends
if ( IRelationship ( pEntity ) < 0 )
{
if ( ! pEntity - > IsPlayer ( ) & & ! g_pGameRules - > IsDeathmatch ( ) )
// ALERT( at_console, "friend\n");
continue ;
}
// can shoot at this one
bestdot = dot ;
bestent = pEdict ;
bestdir = dir ;
}
if ( bestent )
{
bestdir = UTIL_VecToAngles ( bestdir ) ;
bestdir . x = - bestdir . x ;
bestdir = bestdir - pev - > v_angle - pev - > punchangle ;
if ( bestent - > v . takedamage = = DAMAGE_AIM )
m_fOnTarget = TRUE ;
return bestdir ;
}
return Vector ( 0 , 0 , 0 ) ;
}
void CBasePlayer : : ResetAutoaim ( )
{
if ( m_vecAutoAim . x ! = 0 | | m_vecAutoAim . y ! = 0 )
{
m_vecAutoAim = Vector ( 0 , 0 , 0 ) ;
SET_CROSSHAIRANGLE ( edict ( ) , 0 , 0 ) ;
}
m_fOnTarget = FALSE ;
}
/*
= = = = = = = = = = = = =
SetCustomDecalFrames
UNDONE : Determine real frame limit , 8 is a placeholder .
Note : - 1 means no custom frames present .
= = = = = = = = = = = = =
*/
void CBasePlayer : : SetCustomDecalFrames ( int nFrames )
{
if ( nFrames > 0 & &
nFrames < 8 )
m_nCustomSprayFrames = nFrames ;
else
m_nCustomSprayFrames = - 1 ;
}
/*
= = = = = = = = = = = = =
GetCustomDecalFrames
Returns the # of custom frames this player ' s custom clan logo contains .
= = = = = = = = = = = = =
*/
int CBasePlayer : : GetCustomDecalFrames ( void )
{
return m_nCustomSprayFrames ;
}
//=========================================================
// DropPlayerItem - drop the named item, or if no name,
// the active item.
//=========================================================
void CBasePlayer : : DropPlayerItem ( char * pszItemName )
{
if ( ! g_pGameRules - > IsMultiplayer ( ) | | ( weaponstay . value > 0 ) )
{
// no dropping in single player.
return ;
}
if ( ! strlen ( pszItemName ) )
{
// if this string has no length, the client didn't type a name!
// assume player wants to drop the active item.
// make the string null to make future operations in this function easier
pszItemName = NULL ;
}
CBasePlayerItem * pWeapon ;
int i ;
for ( i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
pWeapon = m_rgpPlayerItems [ i ] ;
while ( pWeapon )
{
if ( pszItemName )
{
// try to match by name.
const char * theWeaponClassName = STRING ( pWeapon - > pev - > classname ) ;
if ( ! strcmp ( pszItemName , theWeaponClassName ) )
{
// match!
break ;
}
}
else
{
// trying to drop active item
if ( pWeapon = = m_pActiveItem )
{
// active item!
break ;
}
}
pWeapon = pWeapon - > m_pNext ;
}
// if we land here with a valid pWeapon pointer, that's because we found the
// item we want to drop and hit a BREAK; pWeapon is the item.
if ( pWeapon )
{
g_pGameRules - > GetNextBestWeapon ( this , pWeapon ) ;
UTIL_MakeVectors ( pev - > angles ) ;
pev - > weapons & = ~ ( 1 < < pWeapon - > m_iId ) ; // take item off hud
CWeaponBox * pWeaponBox = ( CWeaponBox * ) CBaseEntity : : Create ( " weaponbox " , pev - > origin + gpGlobals - > v_forward * 10 , pev - > angles , edict ( ) ) ;
pWeaponBox - > pev - > angles . x = 0 ;
pWeaponBox - > pev - > angles . z = 0 ;
pWeaponBox - > PackWeapon ( pWeapon ) ;
pWeaponBox - > pev - > velocity = gpGlobals - > v_forward * 300 + gpGlobals - > v_forward * 100 ;
// drop half of the ammo for this weapon.
int iAmmoIndex ;
iAmmoIndex = GetAmmoIndex ( pWeapon - > pszAmmo1 ( ) ) ; // ???
if ( iAmmoIndex ! = - 1 )
{
// this weapon weapon uses ammo, so pack an appropriate amount.
if ( pWeapon - > iFlags ( ) & ITEM_FLAG_EXHAUSTIBLE )
{
// pack up all the ammo, this weapon is its own ammo type
pWeaponBox - > PackAmmo ( MAKE_STRING ( pWeapon - > pszAmmo1 ( ) ) , m_rgAmmo [ iAmmoIndex ] ) ;
m_rgAmmo [ iAmmoIndex ] = 0 ;
}
else
{
// pack half of the ammo
pWeaponBox - > PackAmmo ( MAKE_STRING ( pWeapon - > pszAmmo1 ( ) ) , m_rgAmmo [ iAmmoIndex ] / 2 ) ;
m_rgAmmo [ iAmmoIndex ] / = 2 ;
}
}
return ; // we're done, so stop searching with the FOR loop.
}
}
}
//=========================================================
// HasPlayerItem Does the player already have this item?
//=========================================================
BOOL CBasePlayer : : HasPlayerItem ( 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 ;
}
//=========================================================
// HasNamedPlayerItem Does the player already have this item?
//=========================================================
CBasePlayerItem * CBasePlayer : : HasNamedPlayerItem ( const char * pszItemName )
{
CBasePlayerItem * pReturn = NULL ;
CBasePlayerItem * pItem = NULL ;
int i ;
for ( i = 0 ; ( i < MAX_ITEM_TYPES ) & & ! pReturn ; i + + )
{
pItem = m_rgpPlayerItems [ i ] ;
while ( pItem & & ! pReturn )
{
if ( ! strcmp ( pszItemName , STRING ( pItem - > pev - > classname ) ) )
{
pReturn = pItem ;
}
pItem = pItem - > m_pNext ;
}
}
return pReturn ;
}
//=========================================================
//
//=========================================================
BOOL CBasePlayer : : SwitchWeapon ( CBasePlayerItem * pWeapon )
{
if ( ! pWeapon - > CanDeploy ( ) )
{
return FALSE ;
}
ResetAutoaim ( ) ;
if ( m_pActiveItem )
{
m_pActiveItem - > Holster ( ) ;
}
m_pActiveItem = pWeapon ;
pWeapon - > Deploy ( ) ;
return TRUE ;
}
//=========================================================
// Dead HEV suit prop
//=========================================================
class CDeadHEV : public CBaseMonster
{
public :
void Spawn ( void ) ;
int Classify ( void ) { return CLASS_HUMAN_MILITARY ; }
void KeyValue ( KeyValueData * pkvd ) ;
int m_iPose ; // which sequence to display -- temporary, don't need to save
static char * m_szPoses [ 4 ] ;
} ;
char * CDeadHEV : : m_szPoses [ ] = { " deadback " , " deadsitting " , " deadstomach " , " deadtable " } ;
void CDeadHEV : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " pose " ) )
{
m_iPose = atoi ( pkvd - > szValue ) ;
pkvd - > fHandled = TRUE ;
}
else
CBaseMonster : : KeyValue ( pkvd ) ;
}
LINK_ENTITY_TO_CLASS ( monster_hevsuit_dead , CDeadHEV ) ;
//=========================================================
// ********** DeadHEV SPAWN **********
//=========================================================
void CDeadHEV : : Spawn ( void )
{
PRECACHE_MODEL ( " models/player.mdl " ) ;
SET_MODEL ( ENT ( pev ) , " models/player.mdl " ) ;
pev - > effects = 0 ;
pev - > yaw_speed = 8 ;
pev - > sequence = 0 ;
pev - > body = 1 ;
m_bloodColor = BLOOD_COLOR_RED ;
pev - > sequence = LookupSequence ( m_szPoses [ m_iPose ] ) ;
if ( pev - > sequence = = - 1 )
{
ALERT ( at_console , " Dead hevsuit with bad pose \n " ) ;
pev - > sequence = 0 ;
pev - > effects = EF_BRIGHTFIELD ;
}
// Corpses have less health
pev - > health = 8 ;
MonsterInitDead ( ) ;
}
class CStripWeapons : public CPointEntity
{
public :
void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
private :
} ;
LINK_ENTITY_TO_CLASS ( player_weaponstrip , CStripWeapons ) ;
void CStripWeapons : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
CBasePlayer * pPlayer = NULL ;
if ( pActivator & & pActivator - > IsPlayer ( ) )
{
pPlayer = ( CBasePlayer * ) pActivator ;
}
else if ( ! g_pGameRules - > IsDeathmatch ( ) )
{
pPlayer = ( CBasePlayer * ) CBaseEntity : : Instance ( g_engfuncs . pfnPEntityOfEntIndex ( 1 ) ) ;
}
if ( pPlayer )
pPlayer - > RemoveAllItems ( FALSE ) ;
}
class CRevertSaved : public CPointEntity
{
public :
void Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value ) ;
void EXPORT MessageThink ( void ) ;
void EXPORT LoadThink ( void ) ;
void KeyValue ( KeyValueData * pkvd ) ;
virtual int Save ( CSave & save ) ;
virtual int Restore ( CRestore & restore ) ;
static TYPEDESCRIPTION m_SaveData [ ] ;
inline float Duration ( void ) { return pev - > dmg_take ; }
inline float HoldTime ( void ) { return pev - > dmg_save ; }
inline float MessageTime ( void ) { return m_messageTime ; }
inline float LoadTime ( void ) { return m_loadTime ; }
inline void SetDuration ( float duration ) { pev - > dmg_take = duration ; }
inline void SetHoldTime ( float hold ) { pev - > dmg_save = hold ; }
inline void SetMessageTime ( float time ) { m_messageTime = time ; }
inline void SetLoadTime ( float time ) { m_loadTime = time ; }
private :
float m_messageTime ;
float m_loadTime ;
} ;
LINK_ENTITY_TO_CLASS ( player_loadsaved , CRevertSaved ) ;
TYPEDESCRIPTION CRevertSaved : : m_SaveData [ ] =
{
DEFINE_FIELD ( CRevertSaved , m_messageTime , FIELD_FLOAT ) , // These are not actual times, but durations, so save as floats
DEFINE_FIELD ( CRevertSaved , m_loadTime , FIELD_FLOAT ) ,
} ;
IMPLEMENT_SAVERESTORE ( CRevertSaved , CPointEntity ) ;
void CRevertSaved : : KeyValue ( KeyValueData * pkvd )
{
if ( FStrEq ( pkvd - > szKeyName , " duration " ) )
{
SetDuration ( atof ( pkvd - > szValue ) ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " holdtime " ) )
{
SetHoldTime ( atof ( pkvd - > szValue ) ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " messagetime " ) )
{
SetMessageTime ( atof ( pkvd - > szValue ) ) ;
pkvd - > fHandled = TRUE ;
}
else if ( FStrEq ( pkvd - > szKeyName , " loadtime " ) )
{
SetLoadTime ( atof ( pkvd - > szValue ) ) ;
pkvd - > fHandled = TRUE ;
}
else
CPointEntity : : KeyValue ( pkvd ) ;
}
void CRevertSaved : : Use ( CBaseEntity * pActivator , CBaseEntity * pCaller , USE_TYPE useType , float value )
{
UTIL_ScreenFadeAll ( pev - > rendercolor , Duration ( ) , HoldTime ( ) , pev - > renderamt , FFADE_OUT ) ;
pev - > nextthink = gpGlobals - > time + MessageTime ( ) ;
SetThink ( & CRevertSaved : : MessageThink ) ;
}
void CRevertSaved : : MessageThink ( void )
{
UTIL_ShowMessageAll ( STRING ( pev - > message ) ) ;
float nextThink = LoadTime ( ) - MessageTime ( ) ;
if ( nextThink > 0 )
{
pev - > nextthink = gpGlobals - > time + nextThink ;
SetThink ( & CRevertSaved : : LoadThink ) ;
}
else
LoadThink ( ) ;
}
void CRevertSaved : : LoadThink ( void )
{
if ( ! gpGlobals - > deathmatch )
{
SERVER_COMMAND ( " reload \n " ) ;
}
}
//=========================================================
// Multiplayer intermission spots.
//=========================================================
class CInfoIntermission : public CPointEntity
{
void Spawn ( void ) ;
void Think ( void ) ;
} ;
void CInfoIntermission : : Spawn ( void )
{
UTIL_SetOrigin ( pev , pev - > origin ) ;
pev - > solid = SOLID_NOT ;
pev - > effects = EF_NODRAW ;
pev - > v_angle = g_vecZero ;
pev - > nextthink = gpGlobals - > time + 2 ; // let targets spawn!
}
void CInfoIntermission : : Think ( void )
{
edict_t * pTarget ;
// find my target
pTarget = FIND_ENTITY_BY_TARGETNAME ( NULL , STRING ( pev - > target ) ) ;
if ( ! FNullEnt ( pTarget ) )
{
pev - > v_angle = UTIL_VecToAngles ( ( pTarget - > v . origin - pev - > origin ) . Normalize ( ) ) ;
pev - > v_angle . x = - pev - > v_angle . x ;
}
}
LINK_ENTITY_TO_CLASS ( info_intermission , CInfoIntermission ) ;