/***
*
* Copyright ( c ) 1996 - 2002 , Valve LLC . All rights reserved .
*
* This product contains software technology licensed from Id
* Software , Inc . ( " Id Technology " ) . Id Technology ( c ) 1996 Id Software , Inc .
* All Rights Reserved .
*
* Use , distribution , and modification of this source code and / or resulting
* object code is restricted to non - commercial enhancements to products from
* Valve LLC . All other use , distribution , or modification is prohibited
* without written permission from Valve LLC .
*
* * * */
/*
= = = = = player . cpp = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
functions dealing with the player
*/
# include "extdll.h"
# include "util.h"
# include "cbase.h"
# include "player.h"
# include "trains.h"
# include "nodes.h"
# include "weapons.h"
# include "monsters.h"
# include "shake.h"
# include "decals.h"
# include "gamerules.h"
# include "game.h"
# include "pm_shared.h"
# include "hltv.h"
# include "quake_gun.h"
// #define DUCKFIX
extern bool g_bHaveMOTD ;
# include "pm_shared.h"
extern DLL_GLOBAL ULONG g_ulModelIndexPlayer ;
extern DLL_GLOBAL BOOL g_fGameOver ;
extern DLL_GLOBAL BOOL g_fDrawLines ;
extern unsigned short g_sTeleport ;
extern unsigned short g_usPowerUp ;
Vector g_vecTeleMins [ MAX_TELES ] ;
Vector g_vecTeleMaxs [ MAX_TELES ] ;
int g_iTeleNum ;
int gEvilImpulse101 ;
extern DLL_GLOBAL int g_iSkillLevel , gDisplayTitle ;
extern " C " int g_bhopcap ;
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 , bool bCheckDM ) ;
extern bool g_bIsThreeWave ;
// 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_flFlashLightTime , FIELD_TIME ) ,
DEFINE_FIELD ( CBasePlayer , m_iFlashBattery , FIELD_INTEGER ) ,
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 ) ,
//DEFINE_FIELD( CBasePlayer, m_fDeadTime, FIELD_FLOAT ), // only used in multiplayer games
//DEFINE_FIELD( CBasePlayer, m_fGameHUDInitialized, FIELD_INTEGER ), // only used in multiplayer games
//DEFINE_FIELD( CBasePlayer, m_flStopExtraSoundTime, FIELD_TIME ),
//DEFINE_FIELD( CBasePlayer, m_fKnownItem, FIELD_INTEGER ), // reset to zero on load
//DEFINE_FIELD( CBasePlayer, m_iPlayerSound, FIELD_INTEGER ), // Don't restore, set in Precache()
//DEFINE_FIELD( CBasePlayer, m_pentSndLast, FIELD_EDICT ), // Don't restore, client needs reset
//DEFINE_FIELD( CBasePlayer, m_flSndRoomtype, FIELD_FLOAT ), // Don't restore, client needs reset
//DEFINE_FIELD( CBasePlayer, m_flSndRange, FIELD_FLOAT ), // Don't restore, client needs reset
//DEFINE_FIELD( CBasePlayer, m_fNewAmmo, FIELD_INTEGER ), // Don't restore, client needs reset
//DEFINE_FIELD( CBasePlayer, m_flgeigerRange, FIELD_FLOAT ), // Don't restore, reset in Precache()
//DEFINE_FIELD( CBasePlayer, m_flgeigerDelay, FIELD_FLOAT ), // Don't restore, reset in Precache()
//DEFINE_FIELD( CBasePlayer, m_igeigerRangePrev, FIELD_FLOAT ), // Don't restore, reset in Precache()
//DEFINE_FIELD( CBasePlayer, m_iStepLeft, FIELD_INTEGER ), // Don't need to restore
//DEFINE_ARRAY( CBasePlayer, m_szTextureName, FIELD_CHARACTER, CBTEXTURENAMEMAX ), // Don't need to restore
//DEFINE_FIELD( CBasePlayer, m_chTextureType, FIELD_CHARACTER ), // Don't need to restore
//DEFINE_FIELD( CBasePlayer, m_fNoPlayerSound, FIELD_BOOLEAN ), // Don't need to restore, debug
//DEFINE_FIELD( CBasePlayer, m_iUpdateTime, FIELD_INTEGER ), // Don't need to restore
//DEFINE_FIELD( CBasePlayer, m_iClientHealth, FIELD_INTEGER ), // Don't restore, client needs reset
//DEFINE_FIELD( CBasePlayer, m_iClientBattery, FIELD_INTEGER ), // Don't restore, client needs reset
//DEFINE_FIELD( CBasePlayer, m_iClientHideHUD, FIELD_INTEGER ), // Don't restore, client needs reset
//DEFINE_FIELD( CBasePlayer, m_fWeapon, FIELD_BOOLEAN ), // Don't restore, client needs reset
//DEFINE_FIELD( CBasePlayer, m_nCustomSprayFrames, FIELD_INTEGER ), // Don't restore, depends on server message after spawning and only matters in multiplayer
//DEFINE_FIELD( CBasePlayer, m_vecAutoAim, FIELD_VECTOR ), // Don't save/restore - this is recomputed
//DEFINE_ARRAY( CBasePlayer, m_rgAmmoLast, FIELD_INTEGER, MAX_AMMO_SLOTS ), // Don't need to restore
//DEFINE_FIELD( CBasePlayer, m_fOnTarget, FIELD_BOOLEAN ), // Don't need to restore
//DEFINE_FIELD( CBasePlayer, m_nCustomSprayFrames, FIELD_INTEGER ), // Don't need to restore
} ;
int giPrecacheGrunt = 0 ;
int gmsgShake = 0 ;
int gmsgFade = 0 ;
int gmsgSelAmmo = 0 ;
int gmsgFlashlight = 0 ;
int gmsgFlashBattery = 0 ;
int gmsgResetHUD = 0 ;
int gmsgInitHUD = 0 ;
int gmsgShowGameTitle = 0 ;
int gmsgCurWeapon = 0 ;
int gmsgHealth = 0 ;
int gmsgDamage = 0 ;
int gmsgBattery = 0 ;
int gmsgTrain = 0 ;
int gmsgLogo = 0 ;
int gmsgWeaponList = 0 ;
int gmsgAmmoX = 0 ;
int gmsgHudText = 0 ;
int gmsgDeathMsg = 0 ;
int gmsgScoreInfo = 0 ;
int gmsgTeamInfo = 0 ;
int gmsgTeamScore = 0 ;
int gmsgGameMode = 0 ;
int gmsgMOTD = 0 ;
int gmsgServerName = 0 ;
int gmsgAmmoPickup = 0 ;
int gmsgWeapPickup = 0 ;
int gmsgItemPickup = 0 ;
int gmsgHideWeapon = 0 ;
int gmsgSetCurWeap = 0 ;
int gmsgSayText = 0 ;
int gmsgTextMsg = 0 ;
int gmsgSetFOV = 0 ;
int gmsgShowMenu = 0 ;
int gmsgGeigerRange = 0 ;
int gmsgTeamNames = 0 ;
int gmsgBhopcap = 0 ;
// QUAKECLASSIC
int gmsgQItems = 0 ;
int gmsgStatusText = 0 ;
int gmsgStatusValue = 0 ;
//++ BulliT
int gmsgAllowSpec = 0 ;
int gmsgSpectator = 0 ;
//-- Martin Webrant
// ThreeWave
int gmsgFlagStatus = 0 ;
int gmsgFlagCarrier = 0 ;
int gmsgRuneStatus = 0 ;
int gmsgCTFMsgs = 0 ;
void LinkUserMessages ( void )
{
// Already taken care of?
if ( gmsgSelAmmo )
{
return ;
}
gmsgSelAmmo = REG_USER_MSG ( " SelAmmo " , sizeof ( SelAmmo ) ) ;
gmsgCurWeapon = REG_USER_MSG ( " CurWeapon " , 3 ) ;
gmsgGeigerRange = REG_USER_MSG ( " Geiger " , 1 ) ;
gmsgFlashlight = REG_USER_MSG ( " Flashlight " , 2 ) ;
gmsgFlashBattery = REG_USER_MSG ( " FlashBat " , 1 ) ;
gmsgHealth = REG_USER_MSG ( " Health " , 1 ) ;
gmsgDamage = REG_USER_MSG ( " Damage " , 12 ) ;
gmsgBattery = REG_USER_MSG ( " Battery " , 2 ) ;
gmsgTrain = REG_USER_MSG ( " Train " , 1 ) ;
//gmsgHudText = REG_USER_MSG( "HudTextPro", -1 );
gmsgHudText = REG_USER_MSG ( " HudText " , - 1 ) ; // we don't use the message but 3rd party addons may!
gmsgSayText = REG_USER_MSG ( " SayText " , - 1 ) ;
gmsgTextMsg = REG_USER_MSG ( " TextMsg " , - 1 ) ;
gmsgWeaponList = REG_USER_MSG ( " WeaponList " , - 1 ) ;
gmsgResetHUD = REG_USER_MSG ( " ResetHUD " , 1 ) ; // called every respawn
gmsgInitHUD = REG_USER_MSG ( " InitHUD " , - 1 ) ; // called every time a new player joins the server
gmsgShowGameTitle = REG_USER_MSG ( " GameTitle " , 1 ) ;
gmsgDeathMsg = REG_USER_MSG ( " DeathMsg " , - 1 ) ;
gmsgScoreInfo = REG_USER_MSG ( " ScoreInfo " , 7 ) ;
gmsgTeamInfo = REG_USER_MSG ( " TeamInfo " , - 1 ) ; // sets the name of a player's team
gmsgTeamScore = REG_USER_MSG ( " TeamScore " , - 1 ) ; // sets the score of a team on the scoreboard
gmsgGameMode = REG_USER_MSG ( " GameMode " , 1 ) ;
gmsgMOTD = REG_USER_MSG ( " MOTD " , - 1 ) ;
gmsgServerName = REG_USER_MSG ( " ServerName " , - 1 ) ;
gmsgAmmoPickup = REG_USER_MSG ( " AmmoPickup " , 2 ) ;
gmsgWeapPickup = REG_USER_MSG ( " WeapPickup " , 1 ) ;
gmsgItemPickup = REG_USER_MSG ( " ItemPickup " , - 1 ) ;
gmsgHideWeapon = REG_USER_MSG ( " HideWeapon " , 1 ) ;
gmsgSetFOV = REG_USER_MSG ( " SetFOV " , 1 ) ;
gmsgShowMenu = REG_USER_MSG ( " ShowMenu " , - 1 ) ;
gmsgShake = REG_USER_MSG ( " ScreenShake " , sizeof ( ScreenShake ) ) ;
gmsgFade = REG_USER_MSG ( " ScreenFade " , sizeof ( ScreenFade ) ) ;
gmsgAmmoX = REG_USER_MSG ( " AmmoX " , 2 ) ;
gmsgTeamNames = REG_USER_MSG ( " TeamNames " , - 1 ) ;
gmsgQItems = REG_USER_MSG ( " QItems " , 4 ) ;
gmsgStatusText = REG_USER_MSG ( " StatusText " , - 1 ) ;
gmsgStatusValue = REG_USER_MSG ( " StatusValue " , 3 ) ;
if ( g_bIsThreeWave )
{
gmsgCTFMsgs = REG_USER_MSG ( " Bonus " , - 1 ) ;
gmsgFlagStatus = REG_USER_MSG ( " FlagStat " , 5 ) ;
gmsgRuneStatus = REG_USER_MSG ( " RuneStat " , 1 ) ;
gmsgFlagCarrier = REG_USER_MSG ( " FlagCarrier " , 2 ) ;
}
//++ BulliT
gmsgAllowSpec = REG_USER_MSG ( " AllowSpec " , 1 ) ; //Allow spectator button message.
gmsgSpectator = REG_USER_MSG ( " Spectator " , 2 ) ; //Spectator message.
//-- Martin Webrant
gmsgBhopcap = REG_USER_MSG ( " Bhopcap " , 1 ) ;
}
LINK_ENTITY_TO_CLASS ( player , CBasePlayer )
// QUAKECLASSIC: Play pain sounds
void CBasePlayer : : Pain ( CBaseEntity * pAttacker )
{
if ( pev - > health < 0 )
return ;
if ( FClassnameIs ( pAttacker - > pev , " teledeath " ) )
{
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/teledth1.wav " , 1 , ATTN_NORM ) ;
return ;
}
// water pain sounds
if ( pev - > watertype = = CONTENT_WATER & & pev - > waterlevel = = 3 )
{
UTIL_Bubbles ( pev - > mins , pev - > maxs , 3 ) ;
if ( RANDOM_FLOAT ( 0 , 1 ) > 0.5 )
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/drown1.wav " , 1 , ATTN_NORM ) ;
else
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/drown2.wav " , 1 , ATTN_NORM ) ;
return ;
}
// slime pain sounds
if ( pev - > watertype = = CONTENT_SLIME )
{
if ( RANDOM_FLOAT ( 0 , 1 ) > 0.5 )
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/lburn1.wav " , 1 , ATTN_NORM ) ;
else
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/lburn2.wav " , 1 , ATTN_NORM ) ;
return ;
}
// lava pain sounds
if ( pev - > watertype = = CONTENT_LAVA )
{
if ( RANDOM_FLOAT ( 0 , 1 ) > 0.5 )
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/lburn1.wav " , 1 , ATTN_NORM ) ;
else
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/lburn2.wav " , 1 , ATTN_NORM ) ;
return ;
}
// don't make multiple pain sounds right after each other
if ( m_flPainSoundFinished > gpGlobals - > time )
{
m_bAxHitMe = 0 ;
return ;
}
m_flPainSoundFinished = gpGlobals - > time + 0.5 ;
// ax pain sound
if ( m_bAxHitMe = = 1 )
{
m_bAxHitMe = 0 ;
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/axhitbod.wav " , 1 , ATTN_NORM ) ;
return ;
}
// play random sound
switch ( ( int ) ( RANDOM_FLOAT ( 0 , 1 ) * 6 ) )
{
case 0 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pain1.wav " , 1 , ATTN_NORM ) ;
break ;
case 1 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pain2.wav " , 1 , ATTN_NORM ) ;
break ;
case 2 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pain3.wav " , 1 , ATTN_NORM ) ;
break ;
case 3 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pain4.wav " , 1 , ATTN_NORM ) ;
break ;
case 4 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pain5.wav " , 1 , ATTN_NORM ) ;
break ;
case 5 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/pain6.wav " , 1 , ATTN_NORM ) ;
break ;
}
}
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 ;
}
// play random sound
switch ( ( int ) ( RANDOM_FLOAT ( 0 , 1 ) * 5 ) )
{
case 0 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/death1.wav " , 1 , ATTN_NORM ) ;
break ;
case 1 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/death2.wav " , 1 , ATTN_NORM ) ;
break ;
case 2 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/death3.wav " , 1 , ATTN_NORM ) ;
break ;
case 3 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/death4.wav " , 1 , ATTN_NORM ) ;
break ;
case 4 :
EMIT_SOUND ( ENT ( pev ) , CHAN_VOICE , " player/death5.wav " , 1 , ATTN_NORM ) ;
break ;
}
}
// override takehealth
// bitsDamageType indicates type of damage healed.
int CBasePlayer : : TakeHealth ( float flHealth , int bitsDamageType )
{
// QUAKECLASSIC
if ( pev - > health < = 0 )
return 0 ;
if ( ! ( bitsDamageType & DMG_IGNORE_MAXHEALTH ) & & ( pev - > health > = pev - > max_health ) )
return 0 ;
int iHealAmount = ceil ( flHealth ) ;
pev - > health + = iHealAmount ;
if ( ! ( bitsDamageType & DMG_IGNORE_MAXHEALTH ) & & ( pev - > health > = pev - > max_health ) )
pev - > health = pev - > max_health ;
if ( pev - > health > 250 )
pev - > health = 250 ;
return 1 ;
}
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 )
{
SpawnBlood ( ptr - > vecEndPos , BloodColor ( ) , flDamage ) ; // a little surface blood.
TraceBleed ( flDamage , vecDir , ptr , bitsDamageType ) ;
AddMultiDamage ( pevAttacker , this , flDamage , bitsDamageType ) ;
}
}
void CBasePlayer : : RemoveAllItems ( BOOL removeSuit )
{
int i ;
CBasePlayerItem * pPendingItem ;
if ( m_pActiveItem )
{
ResetAutoaim ( ) ;
m_pActiveItem - > Holster ( ) ;
m_pActiveItem = NULL ;
}
m_pLastItem = NULL ;
if ( m_pTank ! = 0 )
m_pTank - > Use ( this , this , USE_OFF , 0 ) ;
m_iTrain = TRAIN_NEW ; // turn off train
for ( i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
m_pActiveItem = m_rgpPlayerItems [ i ] ;
m_rgpPlayerItems [ i ] = NULL ;
while ( m_pActiveItem )
{
pPendingItem = m_pActiveItem - > m_pNext ;
m_pActiveItem - > Drop ( ) ;
m_pActiveItem = pPendingItem ;
}
}
m_pActiveItem = NULL ;
pev - > viewmodel = 0 ;
pev - > weaponmodel = 0 ;
if ( removeSuit )
pev - > weapons = 0 ;
else
pev - > weapons & = ~ WEAPON_ALLWEAPONS ;
// Turn off flashlight
ClearBits ( pev - > effects , EF_DIMLIGHT ) ;
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 )
{
// Holster weapon immediately, to allow it to cleanup
if ( m_pActiveItem )
m_pActiveItem - > Holster ( ) ;
g_pGameRules - > PlayerKilled ( this , pevAttacker , g_pevLastInflictor ) ;
if ( m_pTank ! = 0 )
m_pTank - > Use ( this , this , USE_OFF , 0 ) ;
//Remove all powerups and powerup timers
m_flInvincibleFinished = m_flRadsuitFinished = m_flInvisibleFinished = m_flSuperDamageFinished = 0.0 ;
PowerUpThink ( ) ;
SetAnimation ( PLAYER_DIE ) ;
m_iRespawnFrames = 0 ;
pev - > modelindex = g_ulModelIndexPlayer ; // don't use eyes
pev - > deadflag = DEAD_DYING ;
pev - > movetype = MOVETYPE_TOSS ;
ClearBits ( pev - > flags , FL_ONGROUND ) ;
if ( m_iQuakeWeapon = = IT_LIGHTNING )
{
PLAYBACK_EVENT_FULL ( FEV_NOTHOST , edict ( ) , m_usLightning , 0 , ( float * ) & pev - > origin , ( float * ) & pev - > angles , 0.0 , 0.0 , 0 , 1 , 0 , 0 ) ;
if ( m_pActiveItem )
( ( CQuakeGun * ) m_pActiveItem ) - > DestroyEffect ( ) ;
}
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 ;
MESSAGE_BEGIN ( MSG_ONE , gmsgHealth , NULL , pev ) ;
WRITE_BYTE ( m_iClientHealth ) ;
MESSAGE_END ( ) ;
// Tell Ammo Hud that the player is dead
MESSAGE_BEGIN ( MSG_ONE , gmsgCurWeapon , NULL , pev ) ;
WRITE_BYTE ( 0 ) ;
WRITE_BYTE ( 0XFF ) ;
WRITE_BYTE ( 0xFF ) ;
MESSAGE_END ( ) ;
// 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 );
//Quake Player always gibs
if ( pev - > health < - 40 )
{
pev - > solid = SOLID_NOT ;
GibMonster ( ) ; // This clears pev->model
pev - > effects | = EF_NODRAW ;
return ;
}
DeathSound ( ) ;
pev - > angles . x = 0 ;
pev - > angles . z = 0 ;
//++ BulliT
if ( ! g_bIsThreeWave & & g_pGameRules - > m_iGameMode > = ARENA )
m_iQuakeWeapon = 0 ;
//-- Martin Webrant
SetThink ( & CBasePlayer : : PlayerDeathThink ) ;
pev - > nextthink = gpGlobals - > time + 0.1 ;
}
// Set the activity based on an event or current state
void CBasePlayer : : SetAnimation ( PLAYER_ANIM playerAnim )
{
int animDesired ;
float speed ;
char szAnim [ 64 ] ;
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 ( ) ;
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_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
{
m_IdealActivity = ACT_WALK ;
}
break ;
}
switch ( m_IdealActivity )
{
case ACT_HOVER :
case ACT_LEAP :
case ACT_SWIM :
case ACT_HOP :
case ACT_DIESIMPLE :
default :
if ( m_Activity = = m_IdealActivity )
return ;
m_Activity = m_IdealActivity ;
animDesired = LookupActivity ( m_Activity ) ;
// 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 :
if ( FBitSet ( pev - > flags , FL_DUCKING ) ) // crouching
strcpy ( szAnim , " crouch_shoot_ " ) ;
else
strcpy ( szAnim , " ref_shoot_ " ) ;
strcat ( szAnim , m_pszAnimExtention ) ;
animDesired = LookupSequence ( szAnim ) ;
if ( animDesired = = - 1 )
animDesired = 0 ;
if ( pev - > sequence ! = animDesired | | ! m_fSequenceLoops )
{
pev - > frame = 0 ;
}
if ( ! m_fSequenceLoops )
{
pev - > effects | = EF_NOINTERP ;
}
m_Activity = m_IdealActivity ;
pev - > sequence = animDesired ;
ResetSequenceInfo ( ) ;
break ;
case ACT_WALK :
if ( m_Activity ! = ACT_RANGE_ATTACK1 | | m_fSequenceFinished )
{
if ( FBitSet ( pev - > flags , FL_DUCKING ) ) // crouching
strcpy ( szAnim , " crouch_aim_ " ) ;
else
strcpy ( szAnim , " ref_aim_ " ) ;
strcat ( szAnim , m_pszAnimExtention ) ;
animDesired = LookupSequence ( szAnim ) ;
if ( animDesired = = - 1 )
animDesired = 0 ;
m_Activity = ACT_WALK ;
}
else
{
animDesired = pev - > sequence ;
}
}
if ( FBitSet ( pev - > flags , FL_DUCKING ) )
{
if ( speed = = 0 )
{
pev - > gaitsequence = LookupActivity ( ACT_CROUCHIDLE ) ;
// pev->gaitsequence = LookupActivity( ACT_CROUCH );
}
else
{
pev - > gaitsequence = LookupActivity ( ACT_CROUCH ) ;
}
}
else if ( speed > 220 )
{
pev - > gaitsequence = LookupActivity ( ACT_RUN ) ;
}
else if ( speed > 0 )
{
pev - > gaitsequence = LookupActivity ( ACT_WALK ) ;
}
else
{
// pev->gaitsequence = LookupActivity( ACT_WALK );
pev - > gaitsequence = LookupSequence ( " deep_idle " ) ;
}
// Already using the desired animation?
if ( pev - > sequence = = animDesired )
return ;
//ALERT( at_console, "Set animation to %d\n", animDesired );
// Reset to first frame of desired animation
pev - > sequence = animDesired ;
pev - > frame = 0 ;
ResetSequenceInfo ( ) ;
}
/*
= = = = = = = = = = =
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 + = ( int ) 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 )
{
if ( m_iQuakeItems & IT_SUIT )
pev - > dmgtime = gpGlobals - > time + 1.0 ;
else
pev - > dmgtime = gpGlobals - > time + 0.2 ;
TakeDamage ( VARS ( eoNullEntity ) , VARS ( eoNullEntity ) , 10 * pev - > waterlevel , DMG_BURN ) ;
}
}
else if ( pev - > watertype = = CONTENT_SLIME ) // do damage
{
if ( pev - > dmgtime < gpGlobals - > time )
{
pev - > dmgtime = gpGlobals - > time + 1.0 ;
TakeDamage ( VARS ( eoNullEntity ) , VARS ( eoNullEntity ) , 4 * pev - > waterlevel , DMG_ACID ) ;
}
}
if ( ! FBitSet ( pev - > flags , FL_INWATER ) )
{
SetBits ( pev - > flags , FL_INWATER ) ;
pev - > dmgtime = 0 ;
}
}
// 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 ( m_iQuakeWeapon )
{
DropBackpack ( ) ;
}
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 ( m_fSequenceFinished = = 0 )
{
StudioFrameAdvance ( ) ;
return ;
}
if ( pev - > deadflag = = DEAD_DYING )
pev - > deadflag = DEAD_DEAD ;
StopAnimation ( ) ;
pev - > effects | = EF_NOINTERP ;
pev - > framerate = 0.0 ;
//++ BulliT
//Remove sticky dead models.
pev - > solid = SOLID_NOT ;
//-- Martin Webrant
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 ( ) ;
} */
if ( pev - > iuser1 ) // player is in spectator mode
return ;
// wait for any button down, or mp_forcerespawn is set and the respawn time is up
if ( ! fAnyButtonDown & & ! ( g_pGameRules - > IsMultiplayer ( ) & & forcerespawn . value > 0 & & ( gpGlobals - > time > ( m_fDeadTime + 5 ) ) ) )
return ;
pev - > button = 0 ;
m_iRespawnFrames = 0 ;
//ALERT( at_console, "Respawn\n" );
respawn ( pev , ! ( m_afPhysicsFlags & PFLAG_OBSERVER ) ) ; // don't copy a corpse if we're in deathcam.
pev - > nextthink = - 1 ;
}
//=========================================================
// 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 ) ;
UTIL_SetOrigin ( pev , pSpot - > v . origin ) ;
pev - > angles = pev - > v_angle = 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 ) ;
UTIL_SetOrigin ( pev , tr . vecEndPos ) ;
pev - > angles = pev - > v_angle = UTIL_VecToAngles ( tr . vecEndPos - pev - > origin ) ;
}
// start death cam
m_afPhysicsFlags | = PFLAG_OBSERVER ;
pev - > view_ofs = g_vecZero ;
pev - > fixangle = TRUE ;
pev - > solid = SOLID_NOT ;
pev - > takedamage = DAMAGE_NO ;
pev - > movetype = MOVETYPE_NONE ;
pev - > modelindex = 0 ;
}
void CBasePlayer : : StartObserver ( Vector vecPosition , Vector vecViewAngle )
{
m_iHideHUD | = ( HIDEHUD_HEALTH | HIDEHUD_WEAPONS ) ;
m_afPhysicsFlags | = PFLAG_OBSERVER ;
// clear any clientside entities attached to this player
MESSAGE_BEGIN ( MSG_PAS , SVC_TEMPENTITY , pev - > origin ) ;
WRITE_BYTE ( TE_KILLPLAYERATTACHMENTS ) ;
WRITE_BYTE ( ( BYTE ) entindex ( ) ) ;
MESSAGE_END ( ) ;
// Holster weapon immediately, to allow it to cleanup
if ( m_pActiveItem )
m_pActiveItem - > Holster ( ) ;
if ( m_pTank ! = 0 )
m_pTank - > Use ( this , this , USE_OFF , 0 ) ;
// clear out the suit message cache so we don't keep chattering
SetSuitUpdate ( NULL , FALSE , 0 ) ;
// Tell Ammo Hud that the player is dead
MESSAGE_BEGIN ( MSG_ONE , gmsgCurWeapon , NULL , pev ) ;
WRITE_BYTE ( 0 ) ;
WRITE_BYTE ( 0XFF ) ;
WRITE_BYTE ( 0xFF ) ;
MESSAGE_END ( ) ;
// reset FOV
m_iFOV = m_iClientFOV = 0 ;
pev - > fov = m_iFOV ;
MESSAGE_BEGIN ( MSG_ONE , gmsgSetFOV , NULL , pev ) ;
WRITE_BYTE ( 0 ) ;
MESSAGE_END ( ) ;
// Setup flags
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 ;
ClearBits ( m_afPhysicsFlags , PFLAG_DUCKING ) ;
ClearBits ( pev - > flags , FL_DUCKING ) ;
pev - > deadflag = DEAD_RESPAWNABLE ;
pev - > health = 1 ;
// Clear out the status bar
m_fInitHUD = TRUE ;
pev - > team = 0 ;
MESSAGE_BEGIN ( MSG_ALL , gmsgTeamInfo ) ;
WRITE_BYTE ( ENTINDEX ( edict ( ) ) ) ;
WRITE_STRING ( " " ) ;
MESSAGE_END ( ) ;
// Remove all the player's stuff
RemoveAllItems ( FALSE ) ;
// Move them to the new position
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 ( OBS_CHASE_LOCKED ) ;
m_flNextObserverInput = 0 ;
// Find a player to watch
// Observer_SetMode( m_iObserverLastMode );
}
//
// PlayerUse - handles USE keypress
//
# define PLAYER_SEARCH_RADIUS (float)64
void CBasePlayer : : PlayerUse ( void )
{
if ( IsObserver ( ) )
return ;
// Was use pressed or released?
if ( ! ( ( pev - > button | m_afButtonPressed | m_afButtonReleased ) & IN_USE ) )
return ;
// Hit Use on a train?
if ( m_afButtonPressed & IN_USE )
{
if ( m_pTank ! = 0 )
{
// Stop controlling the tank
// TODO: Send HUD Update
m_pTank - > Use ( this , this , USE_OFF , 0 ) ;
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 ( ( int ) 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 ;
// 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 ) ;
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 ) ;
}
}
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 )
{
// If jump pressed within a second of duck while moving, long jump!
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 = ( int ) ( - pev - > frags ) ; // Sum will be 0
}
}
}
pev - > frags + = score ;
MESSAGE_BEGIN ( MSG_ALL , gmsgScoreInfo ) ;
WRITE_BYTE ( ENTINDEX ( edict ( ) ) ) ;
WRITE_SHORT ( ( int ) pev - > frags ) ;
WRITE_SHORT ( m_iDeaths ) ;
WRITE_SHORT ( g_pGameRules - > GetTeamIndex ( m_szTeamName ) + 1 ) ;
MESSAGE_END ( ) ;
}
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 ) ;
}
}
}
}
void CBasePlayer : : PowerUpThink ( void )
{
int iPowerUp = 0 ;
bool bUpdate = FALSE ;
//Quad time ran out
if ( m_iQuakeItems & IT_QUAD & & m_flSuperDamageFinished < gpGlobals - > time )
{
//Clear the glowing shell effect
pev - > renderfx = kRenderFxNone ;
//Reset quad timer
m_flSuperDamageFinished = 0.0 ;
m_bPlayedQuadSound = FALSE ;
//Remove the powerup
m_iQuakeItems & = ~ IT_QUAD ;
//We have other powerups, choose the one with more time remaining
if ( m_iQuakeItems & IT_INVISIBILITY | | m_iQuakeItems & IT_INVULNERABILITY )
{
if ( m_iQuakeItems & IT_INVULNERABILITY )
{
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 255 , 128 , 0 ) ; // RGB
pev - > renderamt = 100 ; // Shell size
iPowerUp = 2 ;
}
if ( m_iQuakeItems & IT_INVISIBILITY )
{
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 128 , 128 , 128 ) ; // RGB
pev - > renderamt = 5 ; // Shell size
}
}
bUpdate = TRUE ;
}
if ( m_iQuakeItems & IT_QUAD & & m_flSuperDamageFinished < = gpGlobals - > time + 3 & & ! m_bPlayedQuadSound )
{
ClientPrint ( pev , HUD_PRINTNOTIFY , " #Quad_Damage_Off " ) ;
EMIT_SOUND ( ENT ( pev ) , CHAN_ITEM , " items/damage2.wav " , 1 , ATTN_NORM ) ;
m_bPlayedQuadSound = TRUE ;
}
//Env Suit time ran out
if ( m_iQuakeItems & IT_SUIT & & m_flRadsuitFinished < gpGlobals - > time )
{
m_flRadsuitFinished = 0.0 ;
m_bPlayedEnvSound = FALSE ;
//Remove the powerup
m_iQuakeItems & = ~ IT_SUIT ;
//We have other powerups, choose the one with more time remaining
if ( m_iQuakeItems & IT_QUAD | | m_iQuakeItems & IT_INVULNERABILITY )
{
if ( m_iQuakeItems & IT_INVULNERABILITY & & m_iQuakeItems & IT_QUAD )
{
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 255 , 125 , 255 ) ; // RGB
pev - > renderamt = 100 ; // Shell size
}
else if ( m_flSuperDamageFinished > m_flInvincibleFinished )
{
pev - > renderfx = kRenderFxNone ;
pev - > rendermode = kRenderNormal ;
pev - > renderamt = 255 ;
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 128 , 128 , 255 ) ; // RGB
pev - > renderamt = 100 ; // Shell size
}
else
{
pev - > renderfx = kRenderFxNone ;
pev - > rendermode = kRenderNormal ;
pev - > renderamt = 255 ;
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 255 , 128 , 0 ) ; // RGB
pev - > renderamt = 100 ; // Shell size
}
}
else //Clear the invisi screen effect
{
pev - > renderfx = kRenderFxNone ;
pev - > rendermode = kRenderNormal ;
pev - > renderamt = 255 ;
}
}
if ( m_iQuakeItems & IT_SUIT & & m_flRadsuitFinished < = gpGlobals - > time + 3 & & ! m_bPlayedEnvSound )
{
ClientPrint ( pev , HUD_PRINTNOTIFY , " #BioSuit_Off " ) ;
EMIT_SOUND ( ENT ( pev ) , CHAN_ITEM , " items/suit2.wav " , 1 , ATTN_NORM ) ;
m_bPlayedEnvSound = TRUE ;
}
//Invisibility time ran out
if ( m_iQuakeItems & IT_INVISIBILITY & & m_flInvisibleFinished < gpGlobals - > time )
{
//Reset the invi timer
m_flInvisibleFinished = 0.0 ;
m_bPlayedProtectSound = FALSE ;
//Remove the powerup
m_iQuakeItems & = ~ IT_INVISIBILITY ;
pev - > renderfx = kRenderFxNone ;
pev - > rendermode = kRenderNormal ;
pev - > renderamt = 255 ;
//We have other powerups, choose the one with more time remaining
if ( m_iQuakeItems & IT_QUAD | | m_iQuakeItems & IT_INVULNERABILITY )
{
if ( m_iQuakeItems & IT_QUAD & & m_iQuakeItems & IT_INVULNERABILITY )
{
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 255 , 125 , 255 ) ; // RGB
pev - > renderamt = 100 ; // Shell size
}
else if ( m_flSuperDamageFinished > m_flInvincibleFinished )
{
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 128 , 128 , 255 ) ; // RGB
pev - > renderamt = 100 ; // Shell size
}
else
{
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 255 , 128 , 0 ) ; // RGB
pev - > renderamt = 100 ; // Shell size
}
}
else //Clear the invisi screen effect
{
pev - > renderfx = kRenderFxNone ;
pev - > rendermode = kRenderNormal ;
pev - > renderamt = 255 ;
}
//Forces our view model to re-appear
W_SetCurrentAmmo ( ) ;
}
if ( m_iQuakeItems & IT_INVISIBILITY & & m_flInvisibleFinished < = gpGlobals - > time + 3 & & ! m_bPlayedInvSound )
{
ClientPrint ( pev , HUD_PRINTNOTIFY , " #Ring_Shadows_Off " ) ;
EMIT_SOUND ( ENT ( pev ) , CHAN_ITEM , " items/inv2.wav " , 1 , ATTN_NORM ) ;
m_bPlayedInvSound = TRUE ;
}
//666 time ran out
if ( m_iQuakeItems & IT_INVULNERABILITY & & m_flInvincibleFinished < gpGlobals - > time )
{
//Clear the glowing shell effect
pev - > renderfx = kRenderFxNone ;
//Reset 666 timer
m_flInvincibleFinished = 0.0 ;
m_bPlayedProtectSound = FALSE ;
//Remove the powerup
m_iQuakeItems & = ~ IT_INVULNERABILITY ;
//We have other powerups, choose the one with more time remaining
if ( m_iQuakeItems & IT_QUAD | | m_iQuakeItems & IT_INVISIBILITY )
{
if ( m_flSuperDamageFinished > m_flInvisibleFinished )
{
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 128 , 128 , 255 ) ; // RGB
pev - > renderamt = 100 ; // Shell size
iPowerUp = 1 ;
}
else
{
pev - > renderfx = kRenderFxGlowShell ;
pev - > rendercolor = Vector ( 128 , 128 , 128 ) ; // RGB
pev - > renderamt = 5 ; // Shell size
}
}
bUpdate = TRUE ;
}
if ( m_iQuakeItems & IT_INVULNERABILITY & & m_flInvincibleFinished < = gpGlobals - > time + 3 & & ! m_bPlayedProtectSound )
{
ClientPrint ( pev , HUD_PRINTNOTIFY , " #Protection_Off " ) ;
EMIT_SOUND ( ENT ( pev ) , CHAN_ITEM , " items/protect2.wav " , 1 , ATTN_NORM ) ;
m_bPlayedProtectSound = TRUE ;
}
if ( bUpdate )
{
W_SetCurrentAmmo ( ) ;
PLAYBACK_EVENT_FULL ( FEV_GLOBAL | FEV_RELIABLE ,
edict ( ) , g_usPowerUp , 0 , ( float * ) & g_vecZero , ( float * ) & g_vecZero ,
( float ) iPowerUp , 0.0 , entindex ( ) , pev - > team , 1 , 0 ) ;
}
}
# 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 ( ) ;
CheckSuitUpdate ( ) ;
// Observer Button Handling
if ( pev - > iuser1 ! = 0 )
{
Observer_HandleButtons ( ) ;
return ;
}
//Run Powerup think (removes powerups over time, etc)
PowerUpThink ( ) ;
// Dead think only if they're not an observer
/*if( IsObserver() )
{
Observer_HandleButtons ( ) ;
Observer_CheckTarget ( ) ;
Observer_CheckProperties ( ) ;
pev - > impulse = 0 ;
return ;
} */
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 ;
int iGearId ; // Vit_amiN: keeps the train control HUD in sync
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 ) ;
}
iGearId = TrainSpeed ( pTrain - > pev - > speed , pTrain - > pev - > impulse ) ;
if ( iGearId ! = ( m_iTrain & 0x0F ) ) // Vit_amiN: speed changed
{
m_iTrain = iGearId ;
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 ( ) ;
}
// QUAKECLASSIC
// Duck removed
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 ( fabs ( 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 = Q_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 ;
MESSAGE_BEGIN ( MSG_ONE , gmsgGeigerRange , NULL , pev ) ;
WRITE_BYTE ( range ) ;
MESSAGE_END ( ) ;
}
// 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 < < IT_AXE ) ) )
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 ( const char * name , int fgroup , int iNoRepeatTime )
{
int i ;
int isentence ;
int iempty = - 1 ;
// Ignore suit updates if no suit
if ( ! ( pev - > weapons & ( 1 < < IT_AXE ) ) )
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
}
void CBasePlayer : : PostThink ( )
{
if ( g_fGameOver )
goto pt_end ; // intermission or finale
if ( ! IsAlive ( ) )
goto pt_end ;
// Handle Tank controlling
if ( m_pTank ! = 0 )
{
// 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 ) ;
}
}
// 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 );
}
else if ( m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED )
{
// 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
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() )
// TODO Make noise
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 ) ;
// Track button info so we can detect 'pressed' and 'released' buttons next frame
m_afButtonLast = pev - > button ;
pt_end :
if ( pev - > deadflag = = DEAD_NO )
m_vecLastViewAngles = pev - > angles ;
else
pev - > angles = m_vecLastViewAngles ;
# if defined( CLIENT_WEAPONS )
// 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 = Q_max ( gun - > m_flNextPrimaryAttack - gpGlobals - > frametime , - 1.0 ) ;
gun - > m_flNextSecondaryAttack = Q_max ( gun - > m_flNextSecondaryAttack - gpGlobals - > frametime , - 0.001 ) ;
if ( gun - > m_flTimeWeaponIdle ! = 1000 )
{
gun - > m_flTimeWeaponIdle = Q_max ( gun - > m_flTimeWeaponIdle - gpGlobals - > frametime , - 0.001 ) ;
}
}
pPlayerItem = pPlayerItem - > m_pNext ;
}
}
}
m_flNextAttack - = gpGlobals - > frametime ;
if ( m_flNextAttack < - 0.001 )
m_flNextAttack = - 0.001 ;
# else
return ;
# endif
}
// checks if the spot is clear of players
BOOL IsSpawnPointValid ( CBaseEntity * pPlayer , CBaseEntity * pSpot )
{
CBaseEntity * ent = NULL ;
if ( ! pSpot - > IsTriggered ( pPlayer ) )
{
return FALSE ;
}
while ( ( ent = UTIL_FindEntityInSphere ( ent , pSpot - > pev - > origin , 96 ) ) ! = NULL )
{
// if ent is a client, don't spawn on 'em
if ( ent - > IsPlayer ( ) & & ent ! = pPlayer )
return FALSE ;
}
return TRUE ;
}
DLL_GLOBAL CBaseEntity * g_pLastSpawn ;
inline int FNullEnt ( CBaseEntity * ent ) { return ( ent = = NULL ) | | FNullEnt ( ent - > edict ( ) ) ; }
/*
= = = = = = = = = = = =
EntSelectSpawnPoint
Returns the entity to spawn at
USES AND SETS GLOBAL g_pLastSpawn
= = = = = = = = = = = =
*/
edict_t * EntSelectSpawnPoint ( CBaseEntity * pPlayer , bool bCheckDM )
{
CBaseEntity * pSpot ;
edict_t * player ;
const char * pszName ;
player = pPlayer - > edict ( ) ;
// choose a info_player_deathmatch point
/*if( g_pGameRules->IsCoOp() )
{
pSpot = UTIL_FindEntityByClassname ( g_pLastSpawn , " info_player_coop " ) ;
if ( ! FNullEnt ( pSpot ) )
goto ReturnSpot ;
pSpot = UTIL_FindEntityByClassname ( g_pLastSpawn , " info_player_start " ) ;
if ( ! FNullEnt ( pSpot ) )
goto ReturnSpot ;
}
else if ( g_pGameRules - > IsDeathmatch ( ) ) */
{
pSpot = g_pLastSpawn ;
// Randomize the start spot
for ( int i = RANDOM_LONG ( 1 , 5 ) ; i > 0 ; i - - )
{
if ( ! bCheckDM )
{
if ( player - > v . team = = 1 )
pszName = " info_player_team1 " ;
else if ( player - > v . team = = 2 )
pszName = " info_player_team2 " ;
else
pszName = " info_player_deathmatch " ;
}
else
pszName = " info_player_deathmatch " ;
pSpot = UTIL_FindEntityByClassname ( pSpot , pszName ) ;
}
if ( FNullEnt ( pSpot ) ) // skip over the null point
{
if ( ! bCheckDM )
{
if ( player - > v . team = = 1 )
pszName = " info_player_team1 " ;
else if ( player - > v . team = = 2 )
pszName = " info_player_team2 " ;
else
pszName = " info_player_deathmatch " ;
}
else
pszName = " info_player_deathmatch " ;
pSpot = UTIL_FindEntityByClassname ( pSpot , pszName ) ;
}
CBaseEntity * pFirstSpot = pSpot ;
do
{
if ( pSpot )
{
// check if pSpot is valid
if ( IsSpawnPointValid ( pPlayer , pSpot ) )
{
if ( pSpot - > pev - > origin = = Vector ( 0 , 0 , 0 ) )
{
if ( ! bCheckDM )
{
if ( player - > v . team = = 1 )
pszName = " info_player_team1 " ;
else if ( player - > v . team = = 2 )
pszName = " info_player_team2 " ;
else
pszName = " info_player_deathmatch " ;
}
else
pszName = " info_player_deathmatch " ;
pSpot = UTIL_FindEntityByClassname ( pSpot , pszName ) ;
continue ;
}
// if so, go to pSpot
goto ReturnSpot ;
}
}
// increment pSpot
if ( ! bCheckDM )
{
if ( player - > v . team = = 1 )
pszName = " info_player_team1 " ;
else if ( player - > v . team = = 2 )
pszName = " info_player_team2 " ;
else
pszName = " info_player_deathmatch " ;
}
else
pszName = " info_player_deathmatch " ;
pSpot = UTIL_FindEntityByClassname ( pSpot , pszName ) ;
} while ( pSpot ! = pFirstSpot ) ; // loop if we're not back to the start
// we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there
if ( ! FNullEnt ( pSpot ) )
{
CBaseEntity * ent = NULL ;
while ( ( ent = UTIL_FindEntityInSphere ( ent , pSpot - > pev - > origin , 128 ) ) ! = NULL )
{
// if ent is a client, kill em (unless they are ourselves)
if ( ent - > IsPlayer ( ) & & ! ( ent - > edict ( ) = = player ) )
ent - > TakeDamage ( VARS ( INDEXENT ( 0 ) ) , VARS ( INDEXENT ( 0 ) ) , 300 , DMG_GENERIC ) ;
}
goto ReturnSpot ;
}
}
// If startspot is set, (re)spawn there.
if ( FStringNull ( gpGlobals - > startspot ) | | ! strlen ( STRING ( gpGlobals - > startspot ) ) )
{
pSpot = UTIL_FindEntityByClassname ( NULL , " info_player_start " ) ;
if ( ! FNullEnt ( pSpot ) )
goto ReturnSpot ;
}
else
{
pSpot = UTIL_FindEntityByTargetname ( NULL , STRING ( gpGlobals - > startspot ) ) ;
if ( ! FNullEnt ( pSpot ) )
goto ReturnSpot ;
}
ReturnSpot :
if ( FNullEnt ( pSpot ) )
{
ALERT ( at_error , " PutClientInServer: no info_player_start on level " ) ;
return INDEXENT ( 0 ) ;
}
g_pLastSpawn = pSpot ;
return pSpot - > edict ( ) ;
}
void CBasePlayer : : Spawn ( void )
{
pev - > classname = MAKE_STRING ( " player " ) ;
pev - > health = 100 ;
pev - > armorvalue = 0 ;
pev - > takedamage = DAMAGE_AIM ;
pev - > solid = SOLID_SLIDEBOX ;
pev - > movetype = MOVETYPE_WALK ;
pev - > max_health = pev - > health ;
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 " ) ;
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 ;
//g_pGameRules->SetDefaultPlayerTeam( this );
g_pGameRules - > GetPlayerSpawnSpot ( this ) ;
SET_MODEL ( ENT ( pev ) , " models/player.mdl " ) ;
g_ulModelIndexPlayer = pev - > modelindex ;
pev - > sequence = LookupActivity ( ACT_IDLE ) ;
UTIL_SetSize ( pev , VEC_HULL_MIN , VEC_HULL_MAX ) ;
pev - > view_ofs = VEC_VIEW ;
Precache ( ) ;
m_HackedGunPos = Vector ( 0 , 32 , 0 ) ;
m_fNoPlayerSound = FALSE ; // normal sound behavior.
m_pLastItem = NULL ;
m_fInitHUD = TRUE ;
m_iClientHideHUD = - 1 ; // force this to be recalculated
m_fWeapon = FALSE ;
m_pClientActiveItem = NULL ;
m_iClientBattery = - 1 ;
m_flLightningTime = 0.0 ;
InitStatusBar ( ) ;
m_iQuakeItems = 0 ;
// 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 ;
if ( ! m_bHadFirstSpawn & & g_bHaveMOTD )
{
pev - > effects | = EF_NODRAW ;
pev - > solid = SOLID_NOT ;
pev - > takedamage = DAMAGE_NO ;
pev - > movetype = MOVETYPE_NONE ;
//++ BulliT
pev - > deadflag = DEAD_RESPAWNABLE ;
//-- Martin Webrant
g_engfuncs . pfnSetClientMaxspeed ( ENT ( pev ) , 1 ) ;
m_iHideHUD | = HIDEHUD_WEAPONS | HIDEHUD_FLASHLIGHT | HIDEHUD_HEALTH ;
}
else
{
g_engfuncs . pfnSetClientMaxspeed ( ENT ( pev ) , PLAYER_MAX_SPEED ) ;
m_iHideHUD & = ~ ( HIDEHUD_WEAPONS | HIDEHUD_HEALTH ) ;
pev - > effects & = ~ EF_NODRAW ;
g_pGameRules - > PlayerSpawn ( this ) ;
//++ BulliT
Spectate_UpdatePosition ( ) ;
//-- Martin Webrant
PLAYBACK_EVENT_FULL ( FEV_GLOBAL , edict ( ) , g_sTeleport , 0.0 , ( float * ) & pev - > origin , ( float * ) & g_vecZero , 0.0 , 0.0 , 0 , 0 , 0 , 0 ) ;
m_fKnownItem = false ;
}
}
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
m_usShotgunSingle = PRECACHE_EVENT ( 1 , " events/shotgun1.sc " ) ;
m_usShotgunDouble = PRECACHE_EVENT ( 1 , " events/shotgun2.sc " ) ;
m_usAxe = PRECACHE_EVENT ( 1 , " events/axe.sc " ) ;
m_usAxeSwing = PRECACHE_EVENT ( 1 , " events/axeswing.sc " ) ;
m_usRocket = PRECACHE_EVENT ( 1 , " events/rocket.sc " ) ;
m_usGrenade = PRECACHE_EVENT ( 1 , " events/grenade.sc " ) ;
m_usLightning = PRECACHE_EVENT ( 1 , " events/lightning.sc " ) ;
m_usSpike = PRECACHE_EVENT ( 1 , " events/spike.sc " ) ;
m_usSuperSpike = PRECACHE_EVENT ( 1 , " events/superspike.sc " ) ;
// 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_flFlashLightTime = 1 ;
m_iTrain | = TRAIN_NEW ;
// Make sure any necessary user messages have been registered
LinkUserMessages ( ) ;
m_iUpdateTime = 5 ; // won't update for 1/2 a second
if ( gInitHUD )
m_fInitHUD = TRUE ;
pev - > fov = m_iFOV ; // Vit_amiN: restore the FOV on level change or map/saved game load
}
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 , TRUE ) ;
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 ) ;
} */
RenewItems ( ) ;
# if defined( CLIENT_WEAPONS )
// 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 ( ) ;
# endif
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 )
return ;
ResetAutoaim ( ) ;
// FIX, this needs to queue them up and delay
if ( m_pActiveItem )
m_pActiveItem - > Holster ( ) ;
m_pLastItem = m_pActiveItem ;
m_pActiveItem = pItem ;
if ( m_pActiveItem )
{
m_pActiveItem - > Deploy ( ) ;
m_pActiveItem - > UpdateItemInfo ( ) ;
}
}
void CBasePlayer : : SelectLastItem ( void )
{
if ( ! m_pLastItem )
{
return ;
}
if ( m_pActiveItem & & ! m_pActiveItem - > CanHolster ( ) )
{
return ;
}
ResetAutoaim ( ) ;
// FIX, this needs to queue them up and delay
if ( m_pActiveItem )
m_pActiveItem - > Holster ( ) ;
CBasePlayerItem * pTemp = m_pActiveItem ;
m_pActiveItem = m_pLastItem ;
m_pLastItem = pTemp ;
m_pActiveItem - > Deploy ( ) ;
m_pActiveItem - > UpdateItemInfo ( ) ;
}
//==============================================
// 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 )
{
}
const char * CBasePlayer : : TeamID ( void )
{
if ( pev = = NULL ) // Not fully connected yet
return " " ;
// return their team name
return m_szTeamName ;
}
//==============================================
// !!!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 , ( int ) 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 ( & CBaseEntity : : SUB_Remove ) ;
pev - > nextthink = gpGlobals - > time + 0.1 ;
}
//==============================================
void CBasePlayer : : GiveNamedItem ( const char * pszName )
{
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 ) ) ;
}
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 ) ;
}
void CBasePlayer : : FlashlightTurnOn ( void )
{
if ( ! g_pGameRules - > FAllowFlashlight ( ) )
{
return ;
}
if ( ( pev - > weapons & ( 1 < < IT_AXE ) ) )
{
EMIT_SOUND_DYN ( ENT ( pev ) , CHAN_WEAPON , SOUND_FLASHLIGHT_ON , 1.0 , 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 , 1.0 , 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_iClientQuakeItems = - 1 ;
m_iClientQuakeWeapon = - 1 ;
m_iClientHideHUD = - 1 ; // Vit_amiN: forcing to update
m_iClientFOV = - 1 ; // Vit_amiN: force client weapons to be sent
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
m_bSentBhopcap = true ; // a1ba: Update bhopcap state
memset ( m_rgAmmoLast , 0 , sizeof ( m_rgAmmoLast ) ) ; // a1ba: Force update AmmoX
// Now force all the necessary messages
// to be sent.
UpdateClientData ( ) ;
}
/*
= = = = = = = = = = = =
ImpulseCommands
= = = = = = = = = = = =
*/
extern float g_flWeaponCheat ;
void CBasePlayer : : ImpulseCommands ( )
{
TraceResult tr ; // UNDONE: kill me! This is temporary for PreAlpha CDs
// Handle use events
PlayerUse ( ) ;
int iImpulse = ( int ) pev - > impulse ;
// QUAKECLASSIC
// Handle weapon switches first
/*if( iImpulse >= 1 && iImpulse <= 8 )
{
W_ChangeWeapon ( iImpulse ) ;
}
else
{ */
switch ( iImpulse )
{
// QUAKECLASSIC
case 10 :
W_CycleWeaponCommand ( ) ;
break ;
case 12 :
W_CycleWeaponReverseCommand ( ) ;
break ;
case 99 :
int iOn ;
if ( ! gmsgLogo )
{
iOn = 1 ;
gmsgLogo = REG_USER_MSG ( " Logo " , 1 ) ;
}
else
{
iOn = 0 ;
}
ASSERT ( gmsgLogo > 0 ) ;
// send "health" update message
MESSAGE_BEGIN ( MSG_ONE , gmsgLogo , NULL , pev ) ;
WRITE_BYTE ( iOn ) ;
MESSAGE_END ( ) ;
if ( ! iOn )
gmsgLogo = 0 ;
break ;
/*case 100:
// temporary flashlight for level designers
if ( FlashlightIsOn ( ) )
{
FlashlightTurnOff ( ) ;
}
else
{
FlashlightTurnOn ( ) ;
}
break ; */
case 201 :
// 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 ;
default :
// check all of the cheat impulse commands now
CheatImpulseCommands ( iImpulse ) ;
break ;
}
//}
pev - > impulse = 0 ;
}
//=========================================================
//=========================================================
void CBasePlayer : : CheatImpulseCommands ( int iImpulse )
{
# if !defined( HLDEMO_BUILD )
if ( g_flWeaponCheat = = 0.0 )
{
return ;
}
CBaseEntity * pEntity ;
TraceResult tr ;
switch ( iImpulse )
{
case 76 :
if ( ! giPrecacheGrunt )
{
giPrecacheGrunt = 1 ;
ALERT ( at_console , " You must now restart to use Grunt-o-matic. \n " ) ;
}
else
{
UTIL_MakeVectors ( Vector ( 0 , pev - > v_angle . y , 0 ) ) ;
Create ( " monster_human_grunt " , pev - > origin + gpGlobals - > v_forward * 128 , pev - > angles ) ;
}
break ;
case 101 :
gEvilImpulse101 = TRUE ;
m_iAmmoNails = 200 ;
m_iAmmoShells = 100 ;
m_iAmmoRockets = 100 ;
m_iAmmoCells = 100 ;
m_iQuakeItems | = IT_NAILGUN | IT_SUPER_NAILGUN | IT_SUPER_SHOTGUN | IT_ROCKET_LAUNCHER | IT_GRENADE_LAUNCHER | IT_LIGHTNING ;
CheckAmmo ( ) ;
W_SetCurrentAmmo ( ) ;
gEvilImpulse101 = FALSE ;
break ;
case 102 :
// Gibbage!!!
CGib : : SpawnRandomGibs ( pev , 1 , 1 ) ;
break ;
case 103 :
// What the hell are you doing?
pEntity = FindEntityForward ( this ) ;
if ( pEntity )
{
CBaseMonster * pMonster = pEntity - > MyMonsterPointer ( ) ;
if ( pMonster )
pMonster - > ReportAIState ( ) ;
}
break ;
case 104 :
// Dump all of the global state varaibles (and global entity names)
gGlobalState . DumpGlobals ( ) ;
break ;
case 105 : // player makes no sound for monsters to hear.
if ( m_fNoPlayerSound )
{
ALERT ( at_console , " Player is audible \n " ) ;
m_fNoPlayerSound = FALSE ;
}
else
{
ALERT ( at_console , " Player is silent \n " ) ;
m_fNoPlayerSound = TRUE ;
}
break ;
case 106 :
// Give me the classname and targetname of this entity.
pEntity = FindEntityForward ( this ) ;
if ( pEntity )
{
ALERT ( at_console , " Classname: %s " , STRING ( pEntity - > pev - > classname ) ) ;
if ( ! FStringNull ( pEntity - > pev - > targetname ) )
{
ALERT ( at_console , " - Targetname: %s \n " , STRING ( pEntity - > pev - > targetname ) ) ;
}
else
{
ALERT ( at_console , " - TargetName: No Targetname \n " ) ;
}
ALERT ( at_console , " Model: %s \n " , STRING ( pEntity - > pev - > model ) ) ;
if ( pEntity - > pev - > globalname )
ALERT ( at_console , " Globalname: %s \n " , STRING ( pEntity - > pev - > globalname ) ) ;
}
break ;
case 107 :
{
//TraceResult tr;
edict_t * pWorld = g_engfuncs . pfnPEntityOfEntIndex ( 0 ) ;
Vector start = pev - > origin + pev - > view_ofs ;
Vector end = start + gpGlobals - > v_forward * 1024 ;
UTIL_TraceLine ( start , end , ignore_monsters , edict ( ) , & tr ) ;
if ( tr . pHit )
pWorld = tr . pHit ;
const char * pTextureName = TRACE_TEXTURE ( pWorld , start , end ) ;
if ( pTextureName )
ALERT ( at_console , " Texture: %s \n " , pTextureName ) ;
}
break ;
case 195 :
// show shortest paths for entire level to nearest node
{
Create ( " node_viewer_fly " , pev - > origin , pev - > angles ) ;
}
break ;
case 196 :
// show shortest paths for entire level to nearest node
{
Create ( " node_viewer_large " , pev - > origin , pev - > angles ) ;
}
break ;
case 197 :
// show shortest paths for entire level to nearest node
{
Create ( " node_viewer_human " , pev - > origin , pev - > angles ) ;
}
break ;
case 199 :
// show nearest node and all connections
{
ALERT ( at_console , " %d \n " , WorldGraph . FindNearestNode ( pev - > origin , bits_NODE_GROUP_REALM ) ) ;
WorldGraph . ShowNodeConnections ( WorldGraph . FindNearestNode ( pev - > origin , bits_NODE_GROUP_REALM ) ) ;
}
break ;
case 202 :
// Random blood splatter
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
CBloodSplat * pBlood = GetClassPtr ( ( CBloodSplat * ) NULL ) ;
pBlood - > Spawn ( pev ) ;
}
break ;
case 203 :
// remove creature.
pEntity = FindEntityForward ( this ) ;
if ( pEntity )
{
if ( pEntity - > pev - > takedamage )
pEntity - > SetThink ( & CBaseEntity : : SUB_Remove ) ;
}
break ;
}
# endif // HLDEMO_BUILD
}
//
// Add a weapon to the player (Item == Weapon == Selectable Object)
//
int CBasePlayer : : AddPlayerItem ( CBasePlayerItem * pItem )
{
CBasePlayerItem * pInsert ;
pInsert = m_rgpPlayerItems [ pItem - > iItemSlot ( ) ] ;
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 , bool bCallHolster )
{
pItem - > pev - > nextthink = 0 ; // crowbar may be trying to swing again, etc.
pItem - > SetThink ( NULL ) ;
if ( m_pActiveItem = = pItem )
{
ResetAutoaim ( ) ;
if ( bCallHolster )
pItem - > Holster ( ) ;
m_pActiveItem = NULL ;
pev - > viewmodel = 0 ;
pev - > weaponmodel = 0 ;
}
// In some cases an item can be both the active and last item, like for instance dropping all weapons and only having an exhaustible weapon left. - Solokiller
if ( m_pLastItem = = pItem )
m_pLastItem = NULL ;
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 , const 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 = Q_min ( iCount , iMax - m_rgAmmo [ i ] ) ;
if ( iAdd < 1 )
return i ;
m_rgAmmo [ i ] + = iAdd ;
if ( gmsgAmmoPickup ) // make sure the ammo messages have been linked first
{
// Send the message that ammo has been picked up
MESSAGE_BEGIN ( MSG_ONE , gmsgAmmoPickup , NULL , pev ) ;
WRITE_BYTE ( GetAmmoIndex ( szName ) ) ; // ammo ID
WRITE_BYTE ( iAdd ) ; // amount
MESSAGE_END ( ) ;
}
return i ;
}
/*
= = = = = = = = = = = =
ItemPreFrame
Called every frame by the player PreThink
= = = = = = = = = = = =
*/
void CBasePlayer : : ItemPreFrame ( )
{
# if defined( CLIENT_WEAPONS )
if ( m_flNextAttack > 0 )
# else
if ( gpGlobals - > time < m_flNextAttack )
# endif
{
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 ! = 0 )
return ;
# if defined( CLIENT_WEAPONS )
// HACKHACK: To make the axe fire 0.3 sec after fire is pressed
// See if the axe should fire now
if ( m_flAxeFire & & m_flAxeFire < = gpGlobals - > time )
{
m_flAxeFire = 0 ;
W_FireAxe ( ) ;
}
if ( m_flNextAttack > 0 )
# else
if ( gpGlobals - > time < m_flNextAttack )
# endif
{
return ;
}
ImpulseCommands ( ) ;
if ( ! m_pActiveItem )
return ;
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 ;
}
// Called from UpdateClientData
// makes sure the client has all the necessary ammo info, if values have changed
void CBasePlayer : : SendAmmoUpdate ( void )
{
for ( int i = 0 ; i < MAX_AMMO_SLOTS ; i + + )
{
if ( m_rgAmmo [ i ] ! = m_rgAmmoLast [ i ] )
{
m_rgAmmoLast [ i ] = m_rgAmmo [ i ] ;
ASSERT ( m_rgAmmo [ i ] > = 0 ) ;
ASSERT ( m_rgAmmo [ i ] < 255 ) ;
// send "Ammo" update message
MESSAGE_BEGIN ( MSG_ONE , gmsgAmmoX , NULL , pev ) ;
WRITE_BYTE ( i ) ;
WRITE_BYTE ( Q_max ( Q_min ( m_rgAmmo [ i ] , 254 ) , 0 ) ) ; // clamp the value to one byte
MESSAGE_END ( ) ;
}
}
}
/*
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
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 )
{
if ( m_fInitHUD )
{
m_fInitHUD = FALSE ;
gInitHUD = FALSE ;
MESSAGE_BEGIN ( MSG_ONE , gmsgResetHUD , NULL , pev ) ;
WRITE_BYTE ( 0 ) ;
MESSAGE_END ( ) ;
if ( ! m_fGameHUDInitialized )
{
MESSAGE_BEGIN ( MSG_ONE , gmsgInitHUD , NULL , pev ) ;
WRITE_BYTE ( g_iTeleNum ) ;
for ( int i = 0 ; i < g_iTeleNum ; i + + )
{
WRITE_COORD ( g_vecTeleMins [ i ] . x ) ;
WRITE_COORD ( g_vecTeleMins [ i ] . y ) ;
WRITE_COORD ( g_vecTeleMins [ i ] . z ) ;
WRITE_COORD ( g_vecTeleMaxs [ i ] . x ) ;
WRITE_COORD ( g_vecTeleMaxs [ i ] . y ) ;
WRITE_COORD ( g_vecTeleMaxs [ i ] . z ) ;
}
CBaseEntity * pEntity = NULL ;
pEntity = UTIL_FindEntityByClassname ( pEntity , " env_fog " ) ;
if ( pEntity )
{
ALERT ( at_console , " Map has fog! \n " ) ;
CClientFog * pFog = ( CClientFog * ) pEntity ;
//Send as bytes?.
WRITE_SHORT ( pFog - > pev - > rendercolor . x ) ;
WRITE_SHORT ( pFog - > pev - > rendercolor . y ) ;
WRITE_SHORT ( pFog - > pev - > rendercolor . z ) ;
WRITE_SHORT ( pFog - > m_iStartDist ) ;
WRITE_SHORT ( pFog - > m_iEndDist ) ;
}
else
ALERT ( at_console , " Map doesn't have any fog! \n " ) ;
MESSAGE_END ( ) ;
g_pGameRules - > InitHUD ( this ) ;
m_fGameHUDInitialized = TRUE ;
//m_iObserverLastMode = OBS_ROAMING;
if ( g_pGameRules - > IsMultiplayer ( ) )
{
FireTargets ( " game_playerjoin " , this , this , USE_TOGGLE , 0 ) ;
}
}
FireTargets ( " game_playerspawn " , this , this , USE_TOGGLE , 0 ) ;
/*
// Send flashlight status
MESSAGE_BEGIN ( MSG_ONE , gmsgFlashlight , NULL , pev ) ;
WRITE_BYTE ( FlashlightIsOn ( ) ? 1 : 0 ) ;
WRITE_BYTE ( m_iFlashBattery ) ;
MESSAGE_END ( ) ;
// Vit_amiN: the geiger state could run out of sync, too
MESSAGE_BEGIN ( MSG_ONE , gmsgGeigerRange , NULL , pev ) ;
WRITE_BYTE ( 0 ) ;
MESSAGE_END ( ) ;
*/
InitStatusBar ( ) ;
}
if ( m_iHideHUD ! = m_iClientHideHUD )
{
MESSAGE_BEGIN ( MSG_ONE , gmsgHideWeapon , NULL , pev ) ;
WRITE_BYTE ( m_iHideHUD ) ;
MESSAGE_END ( ) ;
m_iClientHideHUD = m_iHideHUD ;
}
if ( m_iFOV ! = m_iClientFOV )
{
MESSAGE_BEGIN ( MSG_ONE , gmsgSetFOV , NULL , pev ) ;
WRITE_BYTE ( m_iFOV ) ;
MESSAGE_END ( ) ;
// 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 )
{
MESSAGE_BEGIN ( MSG_ONE , gmsgShowGameTitle , NULL , pev ) ;
WRITE_BYTE ( 0 ) ;
MESSAGE_END ( ) ;
gDisplayTitle = 0 ;
}
if ( pev - > health ! = m_iClientHealth )
{
# define clamp( val, min, max ) ( ((val) > (max)) ? (max) : ( ((val) < (min)) ? (min) : (val) ) )
int iHealth = clamp ( pev - > health , 0 , 255 ) ; // make sure that no negative health values are sent
if ( pev - > health > 0.0f & & pev - > health < = 1.0f )
iHealth = 1 ;
// send "health" update message
MESSAGE_BEGIN ( MSG_ONE , gmsgHealth , NULL , pev ) ;
WRITE_BYTE ( iHealth ) ;
MESSAGE_END ( ) ;
m_iClientHealth = ( int ) pev - > health ;
}
// QUAKECLASSIC
// Send down the state of the QuakeItems
if ( m_iQuakeItems ! = m_iClientQuakeItems )
{
// send "items" update message
MESSAGE_BEGIN ( MSG_ONE , gmsgQItems , NULL , pev ) ;
WRITE_LONG ( m_iQuakeItems ) ;
MESSAGE_END ( ) ;
m_iClientQuakeItems = m_iQuakeItems ;
}
if ( ( int ) pev - > armorvalue ! = m_iClientBattery )
{
m_iClientBattery = ( int ) pev - > armorvalue ;
ASSERT ( gmsgBattery > 0 ) ;
// send "health" update message
MESSAGE_BEGIN ( MSG_ONE , gmsgBattery , NULL , pev ) ;
WRITE_SHORT ( ( int ) pev - > armorvalue ) ;
MESSAGE_END ( ) ;
}
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 & & UTIL_IsValidEntity ( 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 ;
MESSAGE_BEGIN ( MSG_ONE , gmsgDamage , NULL , pev ) ;
WRITE_BYTE ( ( int ) pev - > dmg_save ) ;
WRITE_BYTE ( ( int ) pev - > dmg_take ) ;
WRITE_LONG ( visibleDamageBits ) ;
WRITE_COORD ( damageOrigin . x ) ;
WRITE_COORD ( damageOrigin . y ) ;
WRITE_COORD ( damageOrigin . z ) ;
MESSAGE_END ( ) ;
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 )
{
ASSERT ( gmsgTrain > 0 ) ;
// send "health" update message
MESSAGE_BEGIN ( MSG_ONE , gmsgTrain , NULL , pev ) ;
WRITE_BYTE ( m_iTrain & 0xF ) ;
MESSAGE_END ( ) ;
m_iTrain & = ~ TRAIN_NEW ;
}
int iShellIndex = GetAmmoIndex ( " shells " ) ;
int iNailIndex = GetAmmoIndex ( " nails " ) ;
int iRocketIndex = GetAmmoIndex ( " rockets " ) ;
int iCellIndex = GetAmmoIndex ( " cells " ) ;
//
// New Weapon?
//
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
// QUAKECLASSIC
// Tell the client about the Quake weapons
for ( int i = 1 ; i < 10 ; i + + )
{
const char * pszName ;
int iAmmoIndex = 0 ;
int iMaxAmmo = 0 ;
int iCurrentAmmo = 0 ;
int iBit = 0 ;
switch ( i )
{
case 1 :
iBit = IT_AXE ;
break ;
case 2 :
iBit = IT_SHOTGUN ;
break ;
case 3 :
iBit = IT_SUPER_SHOTGUN ;
break ;
case 4 :
iBit = IT_NAILGUN ;
break ;
case 5 :
iBit = IT_SUPER_NAILGUN ;
break ;
case 6 :
iBit = IT_GRENADE_LAUNCHER ;
break ;
case 7 :
iBit = IT_ROCKET_LAUNCHER ;
break ;
case 8 :
iBit = IT_LIGHTNING ;
break ;
case 9 :
iBit = IT_EXTRA_WEAPON ;
break ;
default :
pszName = " Empty " ;
break ;
}
if ( ! ( m_iQuakeItems & iBit ) )
continue ;
switch ( i )
{
case 1 :
pszName = " weapon_axe " ;
iAmmoIndex = - 1 ;
iBit | = IT_AXE ;
break ;
case 2 :
pszName = " weapon_shotgun " ;
iAmmoIndex = iShellIndex ;
iBit | = IT_SHOTGUN ;
iCurrentAmmo = m_iAmmoShells ;
iMaxAmmo = 100 ;
break ;
case 3 :
pszName = " weapon_doubleshotgun " ;
iAmmoIndex = iShellIndex ;
iBit | = IT_SUPER_SHOTGUN ;
iCurrentAmmo = m_iAmmoShells ;
iMaxAmmo = 100 ;
break ;
case 4 :
pszName = " weapon_nailgun " ;
iAmmoIndex = iNailIndex ;
iBit | = IT_NAILGUN ;
iCurrentAmmo = m_iAmmoNails ;
iMaxAmmo = 200 ;
break ;
case 5 :
pszName = " weapon_supernail " ;
iAmmoIndex = iNailIndex ;
iBit | = IT_SUPER_NAILGUN ;
iCurrentAmmo = m_iAmmoNails ;
iMaxAmmo = 200 ;
break ;
case 6 :
pszName = " weapon_grenadel " ;
iAmmoIndex = iRocketIndex ;
iBit | = IT_GRENADE_LAUNCHER ;
iCurrentAmmo = m_iAmmoRockets ;
iMaxAmmo = 100 ;
break ;
case 7 :
pszName = " weapon_rocketl " ;
iAmmoIndex = iRocketIndex ;
iBit | = IT_ROCKET_LAUNCHER ;
iCurrentAmmo = m_iAmmoRockets ;
iMaxAmmo = 100 ;
break ;
case 8 :
pszName = " weapon_lightning " ;
iAmmoIndex = iCellIndex ;
iBit | = IT_LIGHTNING ;
iCurrentAmmo = m_iAmmoCells ;
iMaxAmmo = 100 ;
break ;
case 9 :
pszName = " weapon_grapple " ;
iAmmoIndex = - 1 ;
iBit | = IT_EXTRA_WEAPON ;
break ;
default :
pszName = " Empty " ;
break ;
}
MESSAGE_BEGIN ( MSG_ONE , gmsgWeaponList , NULL , pev ) ;
WRITE_STRING ( pszName ) ; // string weapon name
WRITE_BYTE ( iAmmoIndex ) ; // byte Ammo Type
WRITE_BYTE ( iMaxAmmo ) ; // byte Max Ammo 1
WRITE_BYTE ( - 1 ) ; // byte Ammo2 Type
WRITE_BYTE ( iCurrentAmmo ) ; // byte Max Ammo 2
WRITE_BYTE ( i ) ; // byte bucket
WRITE_BYTE ( 0 ) ; // byte bucket pos
WRITE_LONG ( iBit ) ; // byte id (bit index into pev->weapons)
if ( i = = 1 | | i = = 9 )
WRITE_BYTE ( 1 ) ; // We can select this one on empty
else
WRITE_BYTE ( 0 ) ; // Can't select it when empty.
MESSAGE_END ( ) ;
}
}
// QUAKECLASSIC
// HACKHACK: Make the HL ammo types equal the Quake ammo
m_rgAmmo [ iShellIndex ] = m_iAmmoShells ;
m_rgAmmo [ iNailIndex ] = m_iAmmoNails ;
m_rgAmmo [ iRocketIndex ] = m_iAmmoRockets ;
m_rgAmmo [ iCellIndex ] = m_iAmmoCells ;
SendAmmoUpdate ( ) ;
// Update all the items
for ( int i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
if ( m_rgpPlayerItems [ i ] ) // each item updates it's successors
m_rgpPlayerItems [ i ] - > UpdateClientData ( this ) ;
}
// Cache and client weapon change
m_pClientActiveItem = m_pActiveItem ;
m_iClientFOV = m_iFOV ;
// Update Status Bar
if ( m_flNextSBarUpdateTime < gpGlobals - > time )
{
UpdateStatusBar ( ) ;
m_flNextSBarUpdateTime = gpGlobals - > time + 0.2 ;
}
// QUAKECLASSIC
m_iClientQuakeWeapon = m_iQuakeWeapon ;
// Send the current bhopcap state.
if ( ! m_bSentBhopcap )
{
m_bSentBhopcap = true ;
MESSAGE_BEGIN ( MSG_ONE , gmsgBhopcap , NULL , pev ) ;
WRITE_BYTE ( g_bhopcap ) ;
MESSAGE_END ( ) ;
}
}
//=========================================================
// 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 = ( int ) m_vecAutoAim . x ;
m_lasty = ( int ) 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.
if ( ! strcmp ( pszItemName , STRING ( pWeapon - > pev - > classname ) ) )
{
// 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 )
{
if ( ! g_pGameRules - > GetNextBestWeapon ( this , pWeapon ) )
return ; // can't drop the item they asked for, may be our last item or something we can't holster
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?
//=========================================================
BOOL CBasePlayer : : HasNamedPlayerItem ( const char * pszItemName )
{
CBasePlayerItem * pItem ;
int i ;
for ( i = 0 ; i < MAX_ITEM_TYPES ; i + + )
{
pItem = m_rgpPlayerItems [ i ] ;
while ( pItem )
{
if ( ! strcmp ( pszItemName , STRING ( pItem - > pev - > classname ) ) )
{
return TRUE ;
}
pItem = pItem - > m_pNext ;
}
}
return FALSE ;
}
//=========================================================
//
//=========================================================
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 const char * m_szPoses [ 4 ] ;
} ;
const 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 ( ) , ( int ) 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 )
//++ BulliT
void CBasePlayer : : Init ( )
{
m_bReady = true ;
m_bIngame = ( g_bIsThreeWave | | g_pGameRules - > m_iGameMode < ARENA ) ;
if ( g_bIsThreeWave )
return ;
if ( g_pGameRules - > m_iGameMode > = LMS )
g_pGameRules - > m_LMS . ClientConnected ( this ) ;
else if ( g_pGameRules - > m_iGameMode = = ARENA )
g_pGameRules - > m_Arena . ClientConnected ( this ) ;
}
void CBasePlayer : : RemoveAllItemsNoClientMessage ( )
{
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 ;
pev - > weapons & = ~ WEAPON_ALLWEAPONS ;
for ( i = 0 ; i < MAX_AMMO_SLOTS ; i + + )
m_rgAmmo [ i ] = 0 ;
// 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 ( ) ;
}
//Special spawn - removes all entites attached to the player.
bool CBasePlayer : : RespawnMatch ( )
{
/*
// clear any clientside entities attached to this player
MESSAGE_BEGIN ( MSG_PAS , SVC_TEMPENTITY , pev - > origin ) ;
WRITE_BYTE ( TE_KILLPLAYERATTACHMENTS ) ;
WRITE_BYTE ( ( BYTE ) entindex ( ) ) ;
MESSAGE_END ( ) ;
*/
//Remove all weapons/items
RemoveAllItemsNoClientMessage ( ) ;
if ( m_pTank ! = 0 )
{
m_pTank - > Use ( this , this , USE_OFF , 0 ) ;
m_pTank = NULL ;
}
//Make sure hud is shown correctly
m_iHideHUD & = ~ HIDEHUD_WEAPONS ;
m_iHideHUD & = ~ HIDEHUD_FLASHLIGHT ;
m_iHideHUD & = ~ HIDEHUD_HEALTH ;
// clear out the suit message cache so we don't keep chattering
SetSuitUpdate ( NULL , FALSE , 0 ) ;
m_iTrain | = TRAIN_NEW ; // Force new train message.
m_fKnownItem = FALSE ; // Force weaponinit messages.
//Respawn player
m_bHadFirstSpawn = true ;
UpdateClientData ( ) ;
Spawn ( ) ;
return true ;
}
void CBasePlayer : : ResetScore ( )
{
//Reset score
pev - > frags = 0.0 ;
m_iDeaths = 0 ;
MESSAGE_BEGIN ( MSG_ALL , gmsgScoreInfo ) ;
WRITE_BYTE ( ENTINDEX ( edict ( ) ) ) ;
WRITE_SHORT ( pev - > frags ) ;
WRITE_SHORT ( m_iDeaths ) ;
WRITE_SHORT ( pev - > team ) ;
MESSAGE_END ( ) ;
}
//-- Martin Webrant