Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4682 lines
119 KiB

/***
9 years ago
*
* 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 "soundent.h"
#include "monsters.h"
#include "shake.h"
#include "decals.h"
#include "gamerules.h"
#include "game.h"
#include "hltv.h"
// #define DUCKFIX
8 years ago
extern DLL_GLOBAL ULONG g_ulModelIndexPlayer;
extern DLL_GLOBAL BOOL g_fGameOver;
extern DLL_GLOBAL BOOL g_fDrawLines;
9 years ago
int gEvilImpulse101;
8 years ago
extern DLL_GLOBAL int g_iSkillLevel, gDisplayTitle;
9 years ago
BOOL gInitHUD = TRUE;
8 years ago
extern void CopyToBodyQue( entvars_t *pev);
extern void respawn( entvars_t *pev, BOOL fCopyCorpse );
extern Vector VecBModelOrigin( entvars_t *pevBModel );
9 years ago
extern edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer );
extern int gmsgZoom;
9 years ago
// the world node graph
8 years ago
extern CGraph WorldGraph;
9 years ago
8 years ago
#define TRAIN_ACTIVE 0x80
9 years ago
#define TRAIN_NEW 0xc0
#define TRAIN_OFF 0x00
8 years ago
#define TRAIN_NEUTRAL 0x01
9 years ago
#define TRAIN_SLOW 0x02
8 years ago
#define TRAIN_MEDIUM 0x03
#define TRAIN_FAST 0x04
9 years ago
#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[] =
9 years ago
{
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 ),
9 years ago
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 ),
9 years ago
//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 gmsgZoom = 0;
9 years ago
int gmsgStatusText = 0;
8 years ago
int gmsgStatusValue = 0;
9 years ago
void LinkUserMessages( void )
{
// Already taken care of?
8 years ago
if( gmsgSelAmmo )
9 years ago
{
return;
}
8 years ago
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 );
9 years ago
gmsgHealth = REG_USER_MSG( "Health", 1 );
gmsgDamage = REG_USER_MSG( "Damage", 12 );
gmsgBattery = REG_USER_MSG( "Battery", 2);
8 years ago
gmsgTrain = REG_USER_MSG( "Train", 1 );
9 years ago
gmsgHudText = REG_USER_MSG( "HudText", -1 );
gmsgSayText = REG_USER_MSG( "SayText", -1 );
gmsgTextMsg = REG_USER_MSG( "TextMsg", -1 );
8 years ago
gmsgWeaponList = REG_USER_MSG( "WeaponList", -1 );
gmsgResetHUD = REG_USER_MSG( "ResetHUD", 1 ); // called every respawn
gmsgInitHUD = REG_USER_MSG( "InitHUD", 0 ); // called every time a new player joins the server
gmsgShowGameTitle = REG_USER_MSG( "GameTitle", 1 );
9 years ago
gmsgDeathMsg = REG_USER_MSG( "DeathMsg", -1 );
gmsgScoreInfo = REG_USER_MSG( "ScoreInfo", 9 );
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 );
8 years ago
gmsgShake = REG_USER_MSG( "ScreenShake", sizeof(ScreenShake) );
gmsgFade = REG_USER_MSG( "ScreenFade", sizeof(ScreenFade) );
gmsgAmmoX = REG_USER_MSG( "AmmoX", 2 );
9 years ago
gmsgTeamNames = REG_USER_MSG( "TeamNames", -1 );
gmsgZoom = REG_USER_MSG( "ZoomHud", 1);
9 years ago
8 years ago
gmsgStatusText = REG_USER_MSG( "StatusText", -1 );
gmsgStatusValue = REG_USER_MSG( "StatusValue", 3 );
9 years ago
}
LINK_ENTITY_TO_CLASS( player, CBasePlayer )
9 years ago
8 years ago
void CBasePlayer::Pain( void )
9 years ago
{
8 years ago
float flRndSound;//sound randomizer
9 years ago
8 years ago
flRndSound = RANDOM_FLOAT( 0, 1 );
8 years ago
if( flRndSound <= 0.33 )
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM );
else if( flRndSound <= 0.66 )
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM );
9 years ago
else
8 years ago
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM );
9 years ago
}
8 years ago
Vector VecVelocityForDamage( float flDamage )
9 years ago
{
8 years ago
Vector vec( RANDOM_FLOAT( -100, 100 ), RANDOM_FLOAT( -100, 100 ), RANDOM_FLOAT( 200, 300 ) );
9 years ago
8 years ago
if( flDamage > -50 )
9 years ago
vec = vec * 0.7;
8 years ago
else if( flDamage > -200 )
9 years ago
vec = vec * 2;
else
vec = vec * 10;
9 years ago
return vec;
}
8 years ago
#if 0
static void ThrowGib( entvars_t *pev, char *szGibModel, float flDamage )
9 years ago
{
edict_t *pentNew = CREATE_ENTITY();
8 years ago
entvars_t *pevNew = VARS( pentNew );
9 years ago
pevNew->origin = pev->origin;
8 years ago
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;
9 years ago
}
8 years ago
static void ThrowHead( entvars_t *pev, char *szGibModel, floatflDamage )
9 years ago
{
8 years ago
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 );
9 years ago
pev->origin.z -= 24;
8 years ago
ClearBits( pev->flags, FL_ONGROUND );
9 years ago
}
#endif
8 years ago
int TrainSpeed( int iSpeed, int iMax )
9 years ago
{
float fSpeed, fMax;
int iRet = 0;
fMax = (float)iMax;
fSpeed = iSpeed;
fSpeed = fSpeed/fMax;
8 years ago
if( iSpeed < 0 )
9 years ago
iRet = TRAIN_BACK;
8 years ago
else if( iSpeed == 0 )
9 years ago
iRet = TRAIN_NEUTRAL;
8 years ago
else if( fSpeed < 0.33 )
9 years ago
iRet = TRAIN_SLOW;
8 years ago
else if( fSpeed < 0.66 )
9 years ago
iRet = TRAIN_MEDIUM;
else
iRet = TRAIN_FAST;
return iRet;
}
8 years ago
void CBasePlayer::DeathSound( void )
9 years ago
{
// water death sounds
/*
8 years ago
if( pev->waterlevel == 3 )
9 years ago
{
8 years ago
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "player/h2odeath.wav", 1, ATTN_NONE );
9 years ago
return;
}
*/
// temporarily using pain sounds for death sounds
8 years ago
switch( RANDOM_LONG( 1, 5 ) )
9 years ago
{
case 1:
8 years ago
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "player/pl_pain5.wav", 1, ATTN_NORM );
9 years ago
break;
case 2:
8 years ago
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "player/pl_pain6.wav", 1, ATTN_NORM );
9 years ago
break;
case 3:
8 years ago
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "player/pl_pain7.wav", 1, ATTN_NORM );
9 years ago
break;
}
// play one of the suit death alarms
8 years ago
EMIT_GROUPNAME_SUIT( ENT( pev ), "HEV_DEAD" );
9 years ago
}
// override takehealth
// bitsDamageType indicates type of damage healed.
8 years ago
int CBasePlayer::TakeHealth( float flHealth, int bitsDamageType )
9 years ago
{
8 years ago
return CBaseMonster::TakeHealth( flHealth, bitsDamageType );
9 years ago
}
8 years ago
Vector CBasePlayer::GetGunPosition()
9 years ago
{
8 years ago
//UTIL_MakeVectors( pev->v_angle );
//m_HackedGunPos = pev->view_ofs;
9 years ago
Vector origin;
9 years ago
origin = pev->origin + pev->view_ofs;
return origin;
}
//=========================================================
// TraceAttack
//=========================================================
8 years ago
void CBasePlayer::TraceAttack( entvars_t *pevAttacker, float flDamage, Vector vecDir, TraceResult *ptr, int bitsDamageType )
9 years ago
{
8 years ago
if( pev->takedamage )
9 years ago
{
m_LastHitGroup = ptr->iHitgroup;
8 years ago
switch( ptr->iHitgroup )
9 years ago
{
case HITGROUP_GENERIC:
break;
case HITGROUP_HEAD:
flDamage *= gSkillData.plrHead;
break;
case HITGROUP_CHEST:
flDamage *= gSkillData.plrChest;
break;
case HITGROUP_STOMACH:
flDamage *= gSkillData.plrStomach;
break;
case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM:
flDamage *= gSkillData.plrArm;
break;
case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG:
flDamage *= gSkillData.plrLeg;
break;
default:
break;
}
8 years ago
SpawnBlood( ptr->vecEndPos, BloodColor(), flDamage );// a little surface blood.
9 years ago
TraceBleed( flDamage, vecDir, ptr, bitsDamageType );
AddMultiDamage( pevAttacker, this, flDamage, bitsDamageType );
}
}
/*
Take some damage.
NOTE: each call to TakeDamage with bitsDamageType set to a time-based damage
type will cause the damage time countdown to be reset. Thus the ongoing effects of poison, radiation
etc are implemented with subsequent calls to TakeDamage using DMG_GENERIC.
*/
8 years ago
#define ARMOR_RATIO 0.2 // Armor Takes 80% of the damage
#define ARMOR_BONUS 0.5 // Each Point of Armor is work 1/x points of health
9 years ago
8 years ago
int CBasePlayer::TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType )
9 years ago
{
// have suit diagnose the problem - ie: report damage type
int bitsDamage = bitsDamageType;
int ffound = TRUE;
int fmajor;
int fcritical;
int fTookDamage;
int ftrivial;
float flRatio;
float flBonus;
float flHealthPrev = pev->health;
flBonus = ARMOR_BONUS;
flRatio = ARMOR_RATIO;
8 years ago
if( ( bitsDamageType & DMG_BLAST ) && g_pGameRules->IsMultiplayer() )
9 years ago
{
// blasts damage armor more.
flBonus *= 2;
}
// Already dead
8 years ago
if( !IsAlive() )
9 years ago
return 0;
8 years ago
// go take the damage first
CBaseEntity *pAttacker = CBaseEntity::Instance( pevAttacker );
8 years ago
if( !g_pGameRules->FPlayerCanTakeDamage( this, pAttacker ) )
9 years ago
{
// Refuse the damage
return 0;
}
// keep track of amount of damage last sustained
m_lastDamageAmount = flDamage;
// Armor.
8 years ago
if( pev->armorvalue && !( bitsDamageType & ( DMG_FALL | DMG_DROWN ) ) )// armor doesn't protect against fall or drown damage!
9 years ago
{
float flNew = flDamage * flRatio;
float flArmor;
8 years ago
flArmor = ( flDamage - flNew ) * flBonus;
9 years ago
// Does this use more armor than we have?
8 years ago
if( flArmor > pev->armorvalue )
9 years ago
{
flArmor = pev->armorvalue;
8 years ago
flArmor *= ( 1 / flBonus );
9 years ago
flNew = flDamage - flArmor;
pev->armorvalue = 0;
}
else
pev->armorvalue -= flArmor;
8 years ago
9 years ago
flDamage = flNew;
}
// this cast to INT is critical!!! If a player ends up with 0.5 health, the engine will get that
// as an int (zero) and think the player is dead! (this will incite a clientside screentilt, etc)
8 years ago
fTookDamage = CBaseMonster::TakeDamage( pevInflictor, pevAttacker, (int)flDamage, bitsDamageType );
9 years ago
// reset damage time countdown for each type of time based damage player just sustained
{
8 years ago
for( int i = 0; i < CDMG_TIMEBASED; i++ )
if( bitsDamageType & ( DMG_PARALYZE << i ) )
9 years ago
m_rgbTimeBasedDamage[i] = 0;
}
// tell director about it
MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR );
8 years ago
WRITE_BYTE( 9 ); // command length in bytes
WRITE_BYTE( DRC_CMD_EVENT ); // take damage event
WRITE_SHORT( ENTINDEX( this->edict() ) ); // index number of primary entity
WRITE_SHORT( ENTINDEX( ENT( pevInflictor ) ) ); // index number of secondary entity
9 years ago
WRITE_LONG( 5 ); // eventflags (priority and flags)
MESSAGE_END();
// how bad is it, doc?
8 years ago
ftrivial = ( pev->health > 75 || m_lastDamageAmount < 5 );
fmajor = ( m_lastDamageAmount > 25 );
fcritical = ( pev->health < 30 );
9 years ago
// handle all bits set in this damage message,
// let the suit give player the diagnosis
// UNDONE: add sounds for types of damage sustained (ie: burn, shock, slash )
// UNDONE: still need to record damage and heal messages for the following types
8 years ago
// DMG_BURN
9 years ago
// DMG_FREEZE
// DMG_BLAST
// DMG_SHOCK
m_bitsDamageType |= bitsDamage; // Save this so we can report it to the client
m_bitsHUDDamage = -1; // make sure the damage bits get resent
8 years ago
while( fTookDamage && ( !ftrivial || ( bitsDamage & DMG_TIMEBASED ) ) && ffound && bitsDamage )
9 years ago
{
ffound = FALSE;
8 years ago
if( bitsDamage & DMG_CLUB )
9 years ago
{
8 years ago
if( fmajor )
SetSuitUpdate( "!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC ); // minor fracture
9 years ago
bitsDamage &= ~DMG_CLUB;
ffound = TRUE;
}
8 years ago
if( bitsDamage & ( DMG_FALL | DMG_CRUSH ) )
9 years ago
{
8 years ago
if( fmajor )
SetSuitUpdate( "!HEV_DMG5", FALSE, SUIT_NEXT_IN_30SEC ); // major fracture
9 years ago
else
8 years ago
SetSuitUpdate( "!HEV_DMG4", FALSE, SUIT_NEXT_IN_30SEC ); // minor fracture
8 years ago
bitsDamage &= ~( DMG_FALL | DMG_CRUSH );
9 years ago
ffound = TRUE;
}
8 years ago
if( bitsDamage & DMG_BULLET )
9 years ago
{
8 years ago
if( m_lastDamageAmount > 5 )
SetSuitUpdate( "!HEV_DMG6", FALSE, SUIT_NEXT_IN_30SEC ); // blood loss detected
9 years ago
//else
8 years ago
// SetSuitUpdate( "!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC ); // minor laceration
9 years ago
bitsDamage &= ~DMG_BULLET;
ffound = TRUE;
}
8 years ago
if( bitsDamage & DMG_SLASH )
9 years ago
{
8 years ago
if( fmajor )
SetSuitUpdate( "!HEV_DMG1", FALSE, SUIT_NEXT_IN_30SEC ); // major laceration
9 years ago
else
8 years ago
SetSuitUpdate( "!HEV_DMG0", FALSE, SUIT_NEXT_IN_30SEC ); // minor laceration
9 years ago
bitsDamage &= ~DMG_SLASH;
ffound = TRUE;
}
8 years ago
if( bitsDamage & DMG_SONIC )
9 years ago
{
8 years ago
if( fmajor )
SetSuitUpdate( "!HEV_DMG2", FALSE, SUIT_NEXT_IN_1MIN ); // internal bleeding
9 years ago
bitsDamage &= ~DMG_SONIC;
ffound = TRUE;
}
8 years ago
if( bitsDamage & ( DMG_POISON | DMG_PARALYZE ) )
9 years ago
{
8 years ago
SetSuitUpdate( "!HEV_DMG3", FALSE, SUIT_NEXT_IN_1MIN ); // blood toxins detected
bitsDamage &= ~( DMG_POISON | DMG_PARALYZE );
9 years ago
ffound = TRUE;
}
8 years ago
if( bitsDamage & DMG_ACID )
9 years ago
{
8 years ago
SetSuitUpdate( "!HEV_DET1", FALSE, SUIT_NEXT_IN_1MIN ); // hazardous chemicals detected
9 years ago
bitsDamage &= ~DMG_ACID;
ffound = TRUE;
}
8 years ago
if( bitsDamage & DMG_NERVEGAS )
9 years ago
{
8 years ago
SetSuitUpdate( "!HEV_DET0", FALSE, SUIT_NEXT_IN_1MIN ); // biohazard detected
9 years ago
bitsDamage &= ~DMG_NERVEGAS;
ffound = TRUE;
}
8 years ago
if( bitsDamage & DMG_RADIATION )
9 years ago
{
8 years ago
SetSuitUpdate( "!HEV_DET2", FALSE, SUIT_NEXT_IN_1MIN ); // radiation detected
9 years ago
bitsDamage &= ~DMG_RADIATION;
ffound = TRUE;
}
8 years ago
if( bitsDamage & DMG_SHOCK )
9 years ago
{
bitsDamage &= ~DMG_SHOCK;
ffound = TRUE;
}
}
pev->punchangle.x = -2;
8 years ago
if( fTookDamage && !ftrivial && fmajor && flHealthPrev >= 75 )
9 years ago
{
// first time we take major damage...
// turn automedic on if not on
8 years ago
SetSuitUpdate( "!HEV_MED1", FALSE, SUIT_NEXT_IN_30MIN ); // automedic on
9 years ago
// give morphine shot if not given recently
8 years ago
SetSuitUpdate( "!HEV_HEAL7", FALSE, SUIT_NEXT_IN_30MIN ); // morphine shot
9 years ago
}
8 years ago
if( fTookDamage && !ftrivial && fcritical && flHealthPrev < 75 )
9 years ago
{
// already took major damage, now it's critical...
8 years ago
if( pev->health < 6 )
SetSuitUpdate( "!HEV_HLTH3", FALSE, SUIT_NEXT_IN_10MIN ); // near death
else if( pev->health < 20 )
SetSuitUpdate( "!HEV_HLTH2", FALSE, SUIT_NEXT_IN_10MIN ); // health critical
9 years ago
// give critical health warnings
8 years ago
if( !RANDOM_LONG( 0, 3 ) && flHealthPrev < 50 )
SetSuitUpdate( "!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN ); //seek medical attention
9 years ago
}
// if we're taking time based damage, warn about its continuing effects
8 years ago
if( fTookDamage && ( bitsDamageType & DMG_TIMEBASED ) && flHealthPrev < 75 )
{
if( flHealthPrev < 50 )
9 years ago
{
8 years ago
if( !RANDOM_LONG( 0, 3 ) )
SetSuitUpdate( "!HEV_DMG7", FALSE, SUIT_NEXT_IN_5MIN ); //seek medical attention
9 years ago
}
8 years ago
else
SetSuitUpdate( "!HEV_HLTH1", FALSE, SUIT_NEXT_IN_10MIN ); // health dropping
}
9 years ago
return fTookDamage;
}
//=========================================================
// PackDeadPlayerItems - call this when a player dies to
// pack up the appropriate weapons and ammo items, and to
// destroy anything that shouldn't be packed.
//
// This is pretty brute force :(
//=========================================================
void CBasePlayer::PackDeadPlayerItems( void )
{
int iWeaponRules;
int iAmmoRules;
int i;
CBasePlayerWeapon *rgpPackWeapons[20] = {0};// 20 hardcoded for now. How to determine exactly how many weapons we have?
8 years ago
int iPackAmmo[MAX_AMMO_SLOTS + 1];
9 years ago
int iPW = 0;// index into packweapons array
int iPA = 0;// index into packammo array
8 years ago
memset( iPackAmmo, -1, sizeof(iPackAmmo) );
9 years ago
8 years ago
// get the game rules
9 years ago
iWeaponRules = g_pGameRules->DeadPlayerWeapons( this );
iAmmoRules = g_pGameRules->DeadPlayerAmmo( this );
8 years ago
if( iWeaponRules == GR_PLR_DROP_GUN_NO && iAmmoRules == GR_PLR_DROP_AMMO_NO )
9 years ago
{
// nothing to pack. Remove the weapons and return. Don't call create on the box!
RemoveAllItems( TRUE );
return;
}
// go through all of the weapons and make a list of the ones to pack
8 years ago
for( i = 0; i < MAX_ITEM_TYPES; i++ )
9 years ago
{
8 years ago
if( m_rgpPlayerItems[i] )
9 years ago
{
// there's a weapon here. Should I pack it?
8 years ago
CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[i];
9 years ago
8 years ago
while( pPlayerItem )
9 years ago
{
switch( iWeaponRules )
{
case GR_PLR_DROP_GUN_ACTIVE:
8 years ago
if( m_pActiveItem && pPlayerItem == m_pActiveItem )
9 years ago
{
// this is the active item. Pack it.
8 years ago
rgpPackWeapons[iPW++] = (CBasePlayerWeapon *)pPlayerItem;
9 years ago
}
break;
case GR_PLR_DROP_GUN_ALL:
8 years ago
rgpPackWeapons[iPW++] = (CBasePlayerWeapon *)pPlayerItem;
9 years ago
break;
default:
break;
}
pPlayerItem = pPlayerItem->m_pNext;
}
}
}
// now go through ammo and make a list of which types to pack.
8 years ago
if( iAmmoRules != GR_PLR_DROP_AMMO_NO )
9 years ago
{
8 years ago
for( i = 0; i < MAX_AMMO_SLOTS; i++ )
9 years ago
{
8 years ago
if( m_rgAmmo[i] > 0 )
9 years ago
{
// player has some ammo of this type.
8 years ago
switch( iAmmoRules )
9 years ago
{
case GR_PLR_DROP_AMMO_ALL:
8 years ago
iPackAmmo[iPA++] = i;
9 years ago
break;
case GR_PLR_DROP_AMMO_ACTIVE:
8 years ago
if( m_pActiveItem && i == m_pActiveItem->PrimaryAmmoIndex() )
9 years ago
{
// this is the primary ammo type for the active weapon
8 years ago
iPackAmmo[iPA++] = i;
9 years ago
}
8 years ago
else if( m_pActiveItem && i == m_pActiveItem->SecondaryAmmoIndex() )
9 years ago
{
// this is the secondary ammo type for the active weapon
8 years ago
iPackAmmo[iPA++] = i;
9 years ago
}
break;
default:
break;
}
}
}
}
// create a box to pack the stuff into.
9 years ago
CWeaponBox *pWeaponBox = (CWeaponBox *)CBaseEntity::Create( "weaponbox", pev->origin, pev->angles, edict() );
pWeaponBox->pev->angles.x = 0;// don't let weaponbox tilt.
pWeaponBox->pev->angles.z = 0;
pWeaponBox->SetThink( &CWeaponBox::Kill );
pWeaponBox->pev->nextthink = gpGlobals->time + 120;
// back these two lists up to their first elements
9 years ago
iPA = 0;
iPW = 0;
// pack the ammo
8 years ago
while( iPackAmmo[iPA] != -1 )
9 years ago
{
8 years ago
pWeaponBox->PackAmmo( MAKE_STRING( CBasePlayerItem::AmmoInfoArray[iPackAmmo[iPA]].pszName ), m_rgAmmo[iPackAmmo[iPA]] );
9 years ago
iPA++;
}
// now pack all of the items in the lists
8 years ago
while( rgpPackWeapons[iPW] )
9 years ago
{
// weapon unhooked from the player. Pack it into der box.
8 years ago
pWeaponBox->PackWeapon( rgpPackWeapons[iPW] );
9 years ago
iPW++;
}
pWeaponBox->pev->velocity = pev->velocity * 1.2;// weaponbox has player's velocity, then some.
RemoveAllItems( TRUE );// now strip off everything that wasn't handled by the code above.
}
void CBasePlayer::RemoveAllItems( BOOL removeSuit )
{
8 years ago
if( m_pActiveItem )
9 years ago
{
8 years ago
ResetAutoaim();
m_pActiveItem->Holster();
9 years ago
m_pActiveItem = NULL;
}
m_pLastItem = NULL;
int i;
CBasePlayerItem *pPendingItem;
8 years ago
for( i = 0; i < MAX_ITEM_TYPES; i++ )
9 years ago
{
m_pActiveItem = m_rgpPlayerItems[i];
8 years ago
while( m_pActiveItem )
9 years ago
{
pPendingItem = m_pActiveItem->m_pNext;
8 years ago
m_pActiveItem->Drop();
9 years ago
m_pActiveItem = pPendingItem;
}
m_rgpPlayerItems[i] = NULL;
}
m_pActiveItem = NULL;
8 years ago
pev->viewmodel = 0;
pev->weaponmodel = 0;
8 years ago
if( removeSuit )
9 years ago
pev->weapons = 0;
else
pev->weapons &= ~WEAPON_ALLWEAPONS;
8 years ago
for( i = 0; i < MAX_AMMO_SLOTS; i++ )
9 years ago
m_rgAmmo[i] = 0;
UpdateClientData();
9 years ago
// send Selected Weapon Message to our client
MESSAGE_BEGIN( MSG_ONE, gmsgCurWeapon, NULL, pev );
8 years ago
WRITE_BYTE( 0 );
WRITE_BYTE( 0 );
WRITE_BYTE( 0 );
9 years ago
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.
9 years ago
void CBasePlayer::Killed( entvars_t *pevAttacker, int iGib )
{
CSound *pSound;
// Holster weapon immediately, to allow it to cleanup
8 years ago
if( m_pActiveItem )
m_pActiveItem->Holster();
9 years ago
g_pGameRules->PlayerKilled( this, pevAttacker, g_pevLastInflictor );
8 years ago
if( m_pTank != NULL )
9 years ago
{
m_pTank->Use( this, this, USE_OFF, 0 );
m_pTank = NULL;
}
// this client isn't going to be thinking for a while, so reset the sound until they respawn
pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) );
{
8 years ago
if( pSound )
9 years ago
{
pSound->Reset();
}
}
SetAnimation( PLAYER_DIE );
9 years ago
m_iRespawnFrames = 0;
pev->modelindex = g_ulModelIndexPlayer; // don't use eyes
8 years ago
pev->deadflag = DEAD_DYING;
pev->movetype = MOVETYPE_TOSS;
9 years ago
ClearBits( pev->flags, FL_ONGROUND );
8 years ago
if( pev->velocity.z < 10 )
pev->velocity.z += RANDOM_FLOAT( 0, 300 );
9 years ago
// clear out the suit message cache so we don't keep chattering
8 years ago
SetSuitUpdate( NULL, FALSE, 0 );
9 years ago
// 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 );
8 years ago
WRITE_BYTE( 0 );
WRITE_BYTE( 0XFF );
WRITE_BYTE( 0xFF );
9 years ago
MESSAGE_END();
// reset FOV
pev->fov = m_iFOV = m_iClientFOV = 0;
MESSAGE_BEGIN( MSG_ONE, gmsgSetFOV, NULL, pev );
8 years ago
WRITE_BYTE( 0 );
9 years ago
MESSAGE_END();
// UNDONE: Put this in, but add FFADE_PERMANENT and make fade time 8.8 instead of 4.12
8 years ago
// UTIL_ScreenFade( edict(), Vector( 128, 0, 0 ), 6, 15, 255, FFADE_OUT | FFADE_MODULATE );
9 years ago
8 years ago
if( ( pev->health < -40 && iGib != GIB_NEVER ) || iGib == GIB_ALWAYS )
9 years ago
{
8 years ago
pev->solid = SOLID_NOT;
9 years ago
GibMonster(); // This clears pev->model
pev->effects |= EF_NODRAW;
return;
}
DeathSound();
9 years ago
pev->angles.x = 0;
pev->angles.z = 0;
SetThink( &CBasePlayer::PlayerDeathThink );
9 years ago
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();
8 years ago
if( pev->flags & FL_FROZEN )
9 years ago
{
speed = 0;
playerAnim = PLAYER_IDLE;
}
8 years ago
switch( playerAnim )
9 years ago
{
case PLAYER_JUMP:
m_IdealActivity = ACT_HOP;
break;
case PLAYER_SUPERJUMP:
m_IdealActivity = ACT_LEAP;
break;
case PLAYER_DIE:
m_IdealActivity = ACT_DIESIMPLE;
8 years ago
m_IdealActivity = GetDeathActivity();
9 years ago
break;
8 years ago
case PLAYER_ATTACK1:
9 years ago
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:
8 years ago
if( !FBitSet( pev->flags, FL_ONGROUND ) && ( m_Activity == ACT_HOP || m_Activity == ACT_LEAP ) ) // Still jumping
9 years ago
{
m_IdealActivity = m_Activity;
}
8 years ago
else if( pev->waterlevel > 1 )
9 years ago
{
8 years ago
if( speed == 0 )
9 years ago
m_IdealActivity = ACT_HOVER;
else
m_IdealActivity = ACT_SWIM;
}
else
{
m_IdealActivity = ACT_WALK;
}
break;
}
8 years ago
switch( m_IdealActivity )
9 years ago
{
case ACT_HOVER:
case ACT_LEAP:
case ACT_SWIM:
case ACT_HOP:
case ACT_DIESIMPLE:
default:
8 years ago
if( m_Activity == m_IdealActivity )
9 years ago
return;
m_Activity = m_IdealActivity;
animDesired = LookupActivity( m_Activity );
9 years ago
// Already using the desired animation?
8 years ago
if( pev->sequence == animDesired )
9 years ago
return;
pev->gaitsequence = 0;
8 years ago
pev->sequence = animDesired;
pev->frame = 0;
ResetSequenceInfo();
9 years ago
return;
case ACT_RANGE_ATTACK1:
8 years ago
if( FBitSet( pev->flags, FL_DUCKING ) ) // crouching
9 years ago
strcpy( szAnim, "crouch_shoot_" );
else
strcpy( szAnim, "ref_shoot_" );
strcat( szAnim, m_szAnimExtention );
animDesired = LookupSequence( szAnim );
8 years ago
if( animDesired == -1 )
9 years ago
animDesired = 0;
8 years ago
if( pev->sequence != animDesired || !m_fSequenceLoops )
9 years ago
{
pev->frame = 0;
}
8 years ago
if( !m_fSequenceLoops )
9 years ago
{
pev->effects |= EF_NOINTERP;
}
m_Activity = m_IdealActivity;
8 years ago
pev->sequence = animDesired;
ResetSequenceInfo();
9 years ago
break;
case ACT_WALK:
8 years ago
if( m_Activity != ACT_RANGE_ATTACK1 || m_fSequenceFinished )
9 years ago
{
8 years ago
if( FBitSet( pev->flags, FL_DUCKING ) ) // crouching
9 years ago
strcpy( szAnim, "crouch_aim_" );
else
strcpy( szAnim, "ref_aim_" );
strcat( szAnim, m_szAnimExtention );
animDesired = LookupSequence( szAnim );
8 years ago
if( animDesired == -1 )
9 years ago
animDesired = 0;
m_Activity = ACT_WALK;
}
else
{
animDesired = pev->sequence;
}
}
8 years ago
if( FBitSet( pev->flags, FL_DUCKING ) )
9 years ago
{
8 years ago
if( speed == 0 )
9 years ago
{
8 years ago
pev->gaitsequence = LookupActivity( ACT_CROUCHIDLE );
// pev->gaitsequence = LookupActivity( ACT_CROUCH );
9 years ago
}
else
{
8 years ago
pev->gaitsequence = LookupActivity( ACT_CROUCH );
9 years ago
}
}
8 years ago
else if( speed > 220 )
9 years ago
{
8 years ago
pev->gaitsequence = LookupActivity( ACT_RUN );
9 years ago
}
8 years ago
else if( speed > 0 )
9 years ago
{
8 years ago
pev->gaitsequence = LookupActivity( ACT_WALK );
9 years ago
}
else
{
8 years ago
// pev->gaitsequence = LookupActivity( ACT_WALK );
pev->gaitsequence = LookupSequence( "deep_idle" );
9 years ago
}
// Already using the desired animation?
8 years ago
if( pev->sequence == animDesired )
9 years ago
return;
//ALERT( at_console, "Set animation to %d\n", animDesired );
// Reset to first frame of desired animation
8 years ago
pev->sequence = animDesired;
pev->frame = 0;
ResetSequenceInfo();
9 years ago
}
/*
===========
TabulateAmmo
This function is used to find and store
all the ammo we have into the ammo vars.
============
*/
void CBasePlayer::TabulateAmmo()
{
ammo_9mm = AmmoInventory( GetAmmoIndex( "9mm" ) );
ammo_357 = AmmoInventory( GetAmmoIndex( "357" ) );
ammo_argrens = AmmoInventory( GetAmmoIndex( "ARgrenades" ) );
ammo_bolts = AmmoInventory( GetAmmoIndex( "bolts" ) );
ammo_buckshot = AmmoInventory( GetAmmoIndex( "buckshot" ) );
ammo_rockets = AmmoInventory( GetAmmoIndex( "rockets" ) );
ammo_uranium = AmmoInventory( GetAmmoIndex( "uranium" ) );
ammo_hornets = AmmoInventory( GetAmmoIndex( "Hornets" ) );
ammo_snipars = AmmoInventory( GetAmmoIndex( "snipars" ) );
9 years ago
}
/*
===========
WaterMove
============
*/
#define AIRTIME 12 // lung full of air lasts this many seconds
void CBasePlayer::WaterMove()
{
int air;
8 years ago
if( pev->movetype == MOVETYPE_NOCLIP )
9 years ago
return;
8 years ago
if( pev->health < 0 )
9 years ago
return;
// waterlevel 0 - not in water
// waterlevel 1 - feet in water
// waterlevel 2 - waist in water
// waterlevel 3 - head in water
8 years ago
if( pev->waterlevel != 3 )
9 years ago
{
// not underwater
9 years ago
// play 'up for air' sound
8 years ago
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 );
9 years ago
pev->air_finished = gpGlobals->time + AIRTIME;
pev->dmg = 2;
// if we took drowning damage, give it back slowly
8 years ago
if( m_idrowndmg > m_idrownrestored )
9 years ago
{
// 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.
9 years ago
// 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;
8 years ago
if( pev->air_finished < gpGlobals->time ) // drown!
9 years ago
{
8 years ago
if( pev->pain_finished < gpGlobals->time )
9 years ago
{
// take drowning damage
pev->dmg += 1;
8 years ago
if( pev->dmg > 5 )
9 years ago
pev->dmg = 5;
8 years ago
TakeDamage( VARS( eoNullEntity ), VARS( eoNullEntity ), pev->dmg, DMG_DROWN );
9 years ago
pev->pain_finished = gpGlobals->time + 1;
9 years ago
// track drowning damage, give it back when
// player finally takes a breath
m_idrowndmg += pev->dmg;
}
}
else
{
m_bitsDamageType &= ~DMG_DROWN;
}
}
8 years ago
if( !pev->waterlevel )
9 years ago
{
8 years ago
if( FBitSet( pev->flags, FL_INWATER ) )
{
ClearBits( pev->flags, FL_INWATER );
9 years ago
}
return;
}
// make bubbles
8 years ago
air = (int)( pev->air_finished - gpGlobals->time );
if( !RANDOM_LONG( 0, 0x1f ) && RANDOM_LONG( 0, AIRTIME - 1 ) >= air )
9 years ago
{
8 years ago
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;
9 years ago
}
}
8 years ago
if( pev->watertype == CONTENT_LAVA ) // do damage
9 years ago
{
8 years ago
if( pev->dmgtime < gpGlobals->time )
TakeDamage( VARS( eoNullEntity ), VARS( eoNullEntity ), 10 * pev->waterlevel, DMG_BURN );
9 years ago
}
8 years ago
else if( pev->watertype == CONTENT_SLIME ) // do damage
9 years ago
{
pev->dmgtime = gpGlobals->time + 1;
8 years ago
TakeDamage( VARS( eoNullEntity ), VARS( eoNullEntity ), 4 * pev->waterlevel, DMG_ACID );
9 years ago
}
8 years ago
if( !FBitSet( pev->flags, FL_INWATER ) )
9 years ago
{
8 years ago
SetBits( pev->flags, FL_INWATER );
9 years ago
pev->dmgtime = 0;
}
}
// TRUE if the player is attached to a ladder
BOOL CBasePlayer::IsOnLadder( void )
{
return ( pev->movetype == MOVETYPE_FLY );
}
8 years ago
void CBasePlayer::PlayerDeathThink( void )
9 years ago
{
float flForward;
8 years ago
if( FBitSet( pev->flags, FL_ONGROUND ) )
9 years ago
{
flForward = pev->velocity.Length() - 20;
8 years ago
if( flForward <= 0 )
9 years ago
pev->velocity = g_vecZero;
else
pev->velocity = flForward * pev->velocity.Normalize();
}
8 years ago
if( HasWeapons() )
9 years ago
{
// we drop the guns here because weapons that have an area effect and can kill their user
// will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
// player class sometimes is freed. It's safer to manipulate the weapons once we know
// we aren't calling into any of their code anymore through the player pointer.
PackDeadPlayerItems();
}
8 years ago
if( pev->modelindex && ( !m_fSequenceFinished ) && ( pev->deadflag == DEAD_DYING ) )
9 years ago
{
8 years ago
StudioFrameAdvance();
9 years ago
m_iRespawnFrames++; // Note, these aren't necessarily real "frames", so behavior is dependent on # of client movement commands
8 years ago
if( m_iRespawnFrames < 120 ) // Animations should be no longer than this
9 years ago
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
8 years ago
if( pev->movetype != MOVETYPE_NONE && FBitSet( pev->flags, FL_ONGROUND ) )
9 years ago
pev->movetype = MOVETYPE_NONE;
8 years ago
if( pev->deadflag == DEAD_DYING )
9 years ago
pev->deadflag = DEAD_DEAD;
9 years ago
StopAnimation();
pev->effects |= EF_NOINTERP;
pev->framerate = 0.0;
8 years ago
BOOL fAnyButtonDown = ( pev->button & ~IN_SCORE );
9 years ago
// wait for all buttons released
8 years ago
if( pev->deadflag == DEAD_DEAD )
9 years ago
{
8 years ago
if( fAnyButtonDown )
9 years ago
return;
8 years ago
if( g_pGameRules->FPlayerCanRespawn( this ) )
9 years ago
{
m_fDeadTime = gpGlobals->time;
pev->deadflag = DEAD_RESPAWNABLE;
}
9 years ago
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.
8 years ago
if( g_pGameRules->IsMultiplayer() && ( gpGlobals->time > ( m_fDeadTime + 6 ) ) && !( m_afPhysicsFlags & PFLAG_OBSERVER ) )
9 years ago
{
// go to dead camera.
StartDeathCam();
}
// wait for any button down, or mp_forcerespawn is set and the respawn time is up
8 years ago
if( !fAnyButtonDown && !( g_pGameRules->IsMultiplayer() && forcerespawn.value > 0 && ( gpGlobals->time > ( m_fDeadTime + 5 ) ) ) )
9 years ago
return;
pev->button = 0;
m_iRespawnFrames = 0;
8 years ago
//ALERT( at_console, "Respawn\n" );
9 years ago
8 years ago
respawn( pev, !( m_afPhysicsFlags & PFLAG_OBSERVER ) );// don't copy a corpse if we're in deathcam.
9 years ago
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;
8 years ago
if( pev->view_ofs == g_vecZero )
9 years ago
{
// don't accept subsequent attempts to StartDeathCam()
return;
}
8 years ago
pSpot = FIND_ENTITY_BY_CLASSNAME( NULL, "info_intermission" );
9 years ago
8 years ago
if( !FNullEnt( pSpot ) )
9 years ago
{
// at least one intermission spot in the world.
iRand = RANDOM_LONG( 0, 3 );
8 years ago
while( iRand > 0 )
9 years ago
{
8 years ago
pNewSpot = FIND_ENTITY_BY_CLASSNAME( pSpot, "info_intermission" );
8 years ago
if( pNewSpot )
9 years ago
{
pSpot = pNewSpot;
}
iRand--;
}
CopyToBodyQue( pev );
StartObserver( pSpot->v.origin, pSpot->v.v_angle );
}
else
{
// no intermission spot. Push them up in the air, looking down at their corpse
TraceResult tr;
CopyToBodyQue( pev );
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, 128 ), ignore_monsters, edict(), &tr );
8 years ago
StartObserver( tr.vecEndPos, UTIL_VecToAngles( tr.vecEndPos - pev->origin ) );
9 years ago
return;
}
}
void CBasePlayer::StartObserver( Vector vecPosition, Vector vecViewAngle )
{
m_afPhysicsFlags |= PFLAG_OBSERVER;
pev->view_ofs = g_vecZero;
pev->angles = pev->v_angle = vecViewAngle;
pev->fixangle = TRUE;
pev->solid = SOLID_NOT;
pev->takedamage = DAMAGE_NO;
pev->movetype = MOVETYPE_NONE;
pev->modelindex = 0;
UTIL_SetOrigin( pev, vecPosition );
}
//
9 years ago
// PlayerUse - handles USE keypress
//
#define PLAYER_SEARCH_RADIUS (float)64
8 years ago
void CBasePlayer::PlayerUse( void )
9 years ago
{
// Was use pressed or released?
8 years ago
if( !( ( pev->button | m_afButtonPressed | m_afButtonReleased) & IN_USE ) )
9 years ago
return;
// Hit Use on a train?
8 years ago
if( m_afButtonPressed & IN_USE )
9 years ago
{
8 years ago
if( m_pTank != NULL )
9 years ago
{
// Stop controlling the tank
// TODO: Send HUD Update
m_pTank->Use( this, this, USE_OFF, 0 );
m_pTank = NULL;
return;
}
else
{
8 years ago
if( m_afPhysicsFlags & PFLAG_ONTRAIN )
9 years ago
{
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
m_iTrain = TRAIN_NEW|TRAIN_OFF;
return;
}
else
{ // Start controlling the train!
CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity );
8 years ago
if( pTrain && !( pev->button & IN_JUMP ) && FBitSet( pev->flags, FL_ONGROUND ) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) && pTrain->OnControls( pev ) )
9 years ago
{
m_afPhysicsFlags |= PFLAG_ONTRAIN;
8 years ago
m_iTrain = TrainSpeed( pTrain->pev->speed, pTrain->pev->impulse );
9 years ago
m_iTrain |= TRAIN_NEW;
8 years ago
EMIT_SOUND( ENT( pev ), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM );
9 years ago
return;
}
}
}
}
CBaseEntity *pObject = NULL;
CBaseEntity *pClosest = NULL;
8 years ago
Vector vecLOS;
9 years ago
float flMaxDot = VIEW_FIELD_NARROW;
float flDot;
8 years ago
UTIL_MakeVectors( pev->v_angle );// so we know which way we are facing
8 years ago
while( ( pObject = UTIL_FindEntityInSphere( pObject, pev->origin, PLAYER_SEARCH_RADIUS ) ) != NULL )
9 years ago
{
8 years ago
if( pObject->ObjectCaps() & ( FCAP_IMPULSE_USE | FCAP_CONTINUOUS_USE | FCAP_ONOFF_USE ) )
9 years ago
{
// !!!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)
8 years ago
vecLOS = ( VecBModelOrigin( pObject->pev ) - ( pev->origin + pev->view_ofs ) );
9 years ago
// 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 );
8 years ago
flDot = DotProduct( vecLOS , gpGlobals->v_forward );
if( flDot > flMaxDot )
{
// only if the item is in front of the user
9 years ago
pClosest = pObject;
flMaxDot = flDot;
//ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot );
9 years ago
}
//ALERT( at_console, "%s : %f\n", STRING( pObject->pev->classname ), flDot );
9 years ago
}
}
pObject = pClosest;
// Found an object
8 years ago
if( pObject )
9 years ago
{
//!!!UNDONE: traceline here to prevent USEing buttons through walls
int caps = pObject->ObjectCaps();
8 years ago
if( m_afButtonPressed & IN_USE )
EMIT_SOUND( ENT(pev), CHAN_ITEM, "common/wpn_select.wav", 0.4, ATTN_NORM );
9 years ago
8 years ago
if( ( ( pev->button & IN_USE ) && ( caps & FCAP_CONTINUOUS_USE ) ) ||
( ( m_afButtonPressed & IN_USE ) && ( caps & ( FCAP_IMPULSE_USE | FCAP_ONOFF_USE ) ) ) )
9 years ago
{
8 years ago
if( caps & FCAP_CONTINUOUS_USE )
9 years ago
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
8 years ago
else if( ( m_afButtonReleased & IN_USE ) && ( pObject->ObjectCaps() & FCAP_ONOFF_USE ) ) // BUGBUG This is an "off" use
9 years ago
{
pObject->Use( this, this, USE_SET, 0 );
}
}
else
{
8 years ago
if( m_afButtonPressed & IN_USE )
EMIT_SOUND( ENT( pev ), CHAN_ITEM, "common/wpn_denyselect.wav", 0.4, ATTN_NORM );
9 years ago
}
}
void CBasePlayer::Jump()
{
8 years ago
Vector vecWallCheckDir;// direction we're tracing a line to find a wall when walljumping
Vector vecAdjustedVelocity;
Vector vecSpot;
TraceResult tr;
8 years ago
if( FBitSet( pev->flags, FL_WATERJUMP ) )
9 years ago
return;
8 years ago
if( pev->waterlevel >= 2 )
9 years ago
{
return;
}
// jump velocity is sqrt( height * gravity * 2)
// If this isn't the first frame pressing the jump button, break out.
8 years ago
if( !FBitSet( m_afButtonPressed, IN_JUMP ) )
9 years ago
return; // don't pogo stick
8 years ago
if( !( pev->flags & FL_ONGROUND ) || !pev->groundentity )
9 years ago
{
return;
}
// many features in this function use v_forward, so makevectors now.
8 years ago
UTIL_MakeVectors( pev->angles );
9 years ago
8 years ago
// ClearBits( pev->flags, FL_ONGROUND ); // don't stairwalk
9 years ago
SetAnimation( PLAYER_JUMP );
8 years ago
if( m_fLongJump &&
( pev->button & IN_DUCK ) &&
9 years ago
( pev->flDuckTime > 0 ) &&
pev->velocity.Length() > 50 )
{
SetAnimation( PLAYER_SUPERJUMP );
}
// If you're standing on a conveyor, add it's velocity to yours (for momentum)
8 years ago
entvars_t *pevGround = VARS( pev->groundentity );
if( pevGround && ( pevGround->flags & FL_CONVEYOR ) )
9 years ago
{
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.
8 years ago
for( int i = 0; i < 18; i++ )
9 years ago
{
UTIL_TraceHull( pPlayer->v.origin, pPlayer->v.origin, dont_ignore_monsters, head_hull, pPlayer, &trace );
8 years ago
if( trace.fStartSolid )
pPlayer->v.origin.z++;
9 years ago
else
break;
}
}
8 years ago
void CBasePlayer::Duck()
9 years ago
{
8 years ago
if( pev->button & IN_DUCK )
9 years ago
{
8 years ago
if( m_IdealActivity != ACT_LEAP )
9 years ago
{
SetAnimation( PLAYER_WALK );
}
}
}
//
// ID's player as such.
//
8 years ago
int CBasePlayer::Classify( void )
9 years ago
{
return CLASS_PLAYER;
}
void CBasePlayer::AddPoints( int score, BOOL bAllowNegativeScore )
{
// Positive score always adds
8 years ago
if( score < 0 )
9 years ago
{
8 years ago
if( !bAllowNegativeScore )
9 years ago
{
8 years ago
if( pev->frags < 0 ) // Can't go more negative
9 years ago
return;
8 years ago
if( -score > pev->frags ) // Will this go negative?
9 years ago
{
score = -pev->frags; // Sum will be 0
}
}
}
pev->frags += score;
MESSAGE_BEGIN( MSG_ALL, gmsgScoreInfo );
8 years ago
WRITE_BYTE( ENTINDEX( edict() ) );
9 years ago
WRITE_SHORT( pev->frags );
WRITE_SHORT( m_iDeaths );
WRITE_SHORT( 0 );
WRITE_SHORT( g_pGameRules->GetTeamIndex( m_szTeamName ) + 1 );
MESSAGE_END();
}
void CBasePlayer::AddPointsToTeam( int score, BOOL bAllowNegativeScore )
{
int index = entindex();
8 years ago
for( int i = 1; i <= gpGlobals->maxClients; i++ )
9 years ago
{
CBaseEntity *pPlayer = UTIL_PlayerByIndex( i );
8 years ago
if( pPlayer && i != index )
9 years ago
{
8 years ago
if( g_pGameRules->PlayerRelationship( this, pPlayer ) == GR_TEAMMATE )
9 years ago
{
pPlayer->AddPoints( score, bAllowNegativeScore );
}
}
}
}
//Player ID
void CBasePlayer::InitStatusBar()
{
m_flStatusBarDisappearDelay = 0;
m_SbarString1[0] = m_SbarString0[0] = 0;
}
void CBasePlayer::UpdateStatusBar()
{
int newSBarState[SBAR_END] = {0};
8 years ago
char sbuf0[SBAR_STRING_SIZE];
9 years ago
char sbuf1[ SBAR_STRING_SIZE ];
strcpy( sbuf0, m_SbarString0 );
strcpy( sbuf1, m_SbarString1 );
// Find an ID Target
TraceResult tr;
UTIL_MakeVectors( pev->v_angle + pev->punchangle );
Vector vecSrc = EyePosition();
8 years ago
Vector vecEnd = vecSrc + ( gpGlobals->v_forward * MAX_ID_RANGE );
UTIL_TraceLine( vecSrc, vecEnd, dont_ignore_monsters, edict(), &tr );
9 years ago
8 years ago
if( tr.flFraction != 1.0 )
9 years ago
{
8 years ago
if( !FNullEnt( tr.pHit ) )
9 years ago
{
CBaseEntity *pEntity = CBaseEntity::Instance( tr.pHit );
8 years ago
if( pEntity->Classify() == CLASS_PLAYER )
9 years ago
{
8 years ago
newSBarState[SBAR_ID_TARGETNAME] = ENTINDEX( pEntity->edict() );
9 years ago
strcpy( sbuf1, "1 %p1\n2 Health: %i2%%\n3 Armor: %i3%%" );
// allies and medics get to see the targets health
8 years ago
if( g_pGameRules->PlayerRelationship( this, pEntity ) == GR_TEAMMATE )
9 years ago
{
8 years ago
newSBarState[SBAR_ID_TARGETHEALTH] = 100 * ( pEntity->pev->health / pEntity->pev->max_health );
newSBarState[SBAR_ID_TARGETARMOR] = pEntity->pev->armorvalue; //No need to get it % based since 100 it's the max.
9 years ago
}
m_flStatusBarDisappearDelay = gpGlobals->time + 1.0;
}
}
8 years ago
else if( m_flStatusBarDisappearDelay > gpGlobals->time )
9 years ago
{
// hold the values for a short amount of time after viewing the object
8 years ago
newSBarState[SBAR_ID_TARGETNAME] = m_izSBarState[SBAR_ID_TARGETNAME];
newSBarState[SBAR_ID_TARGETHEALTH] = m_izSBarState[SBAR_ID_TARGETHEALTH];
newSBarState[SBAR_ID_TARGETARMOR] = m_izSBarState[SBAR_ID_TARGETARMOR];
9 years ago
}
}
BOOL bForceResend = FALSE;
8 years ago
if( strcmp( sbuf0, m_SbarString0 ) )
9 years ago
{
MESSAGE_BEGIN( MSG_ONE, gmsgStatusText, NULL, pev );
WRITE_BYTE( 0 );
WRITE_STRING( sbuf0 );
MESSAGE_END();
strcpy( m_SbarString0, sbuf0 );
// make sure everything's resent
bForceResend = TRUE;
}
8 years ago
if( strcmp( sbuf1, m_SbarString1 ) )
9 years ago
{
MESSAGE_BEGIN( MSG_ONE, gmsgStatusText, NULL, pev );
WRITE_BYTE( 1 );
WRITE_STRING( sbuf1 );
MESSAGE_END();
strcpy( m_SbarString1, sbuf1 );
// make sure everything's resent
bForceResend = TRUE;
}
// Check values and send if they don't match
8 years ago
for( int i = 1; i < SBAR_END; i++ )
9 years ago
{
8 years ago
if( newSBarState[i] != m_izSBarState[i] || bForceResend )
9 years ago
{
MESSAGE_BEGIN( MSG_ONE, gmsgStatusValue, NULL, pev );
WRITE_BYTE( i );
WRITE_SHORT( newSBarState[i] );
MESSAGE_END();
m_izSBarState[i] = newSBarState[i];
}
}
}
8 years ago
#define CLIMB_SHAKE_FREQUENCY 22 // how many frames in between screen shakes when climbing
9 years ago
#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
8 years ago
void CBasePlayer::PreThink( void )
9 years ago
{
8 years ago
int buttonsChanged = ( m_afButtonLast ^ pev->button ); // These buttons have changed this frame
9 years ago
// Debounced button codes for pressed/released
// UNDONE: Do we need auto-repeat?
m_afButtonPressed = buttonsChanged & pev->button; // The changed ones still down are "pressed"
8 years ago
m_afButtonReleased = buttonsChanged & ( ~pev->button ); // The ones not down are "released"
9 years ago
g_pGameRules->PlayerThink( this );
8 years ago
if( g_fGameOver )
9 years ago
return; // intermission or finale
8 years ago
UTIL_MakeVectors( pev->v_angle ); // is this still used?
8 years ago
ItemPreFrame();
9 years ago
WaterMove();
8 years ago
if( g_pGameRules && g_pGameRules->FAllowFlashlight() )
9 years ago
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();
9 years ago
CheckTimeBasedDamage();
CheckSuitUpdate();
8 years ago
if( pev->deadflag >= DEAD_DYING )
9 years ago
{
PlayerDeathThink();
return;
}
// So the correct flags get sent to client asap.
//
8 years ago
if( m_afPhysicsFlags & PFLAG_ONTRAIN )
9 years ago
pev->flags |= FL_ONTRAIN;
else
pev->flags &= ~FL_ONTRAIN;
// Train speed control
8 years ago
if( m_afPhysicsFlags & PFLAG_ONTRAIN )
9 years ago
{
CBaseEntity *pTrain = CBaseEntity::Instance( pev->groundentity );
float vel;
8 years ago
if( !pTrain )
9 years ago
{
TraceResult trainTrace;
// Maybe this is on the other side of a level transition
8 years ago
UTIL_TraceLine( pev->origin, pev->origin + Vector( 0, 0, -38 ), ignore_monsters, ENT( pev ), &trainTrace );
9 years ago
// HACKHACK - Just look for the func_tracktrain classname
8 years ago
if( trainTrace.flFraction != 1.0 && trainTrace.pHit )
9 years ago
pTrain = CBaseEntity::Instance( trainTrace.pHit );
8 years ago
if( !pTrain || !( pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE ) || !pTrain->OnControls( pev ) )
9 years ago
{
//ALERT( at_error, "In train mode with no train!\n" );
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
m_iTrain = TRAIN_NEW|TRAIN_OFF;
return;
}
}
8 years ago
else if( !FBitSet( pev->flags, FL_ONGROUND ) || FBitSet( pTrain->pev->spawnflags, SF_TRACKTRAIN_NOCONTROL ) || ( pev->button & ( IN_MOVELEFT | IN_MOVERIGHT ) ) )
9 years ago
{
// Turn off the train if you jump, strafe, or the train controls go dead
m_afPhysicsFlags &= ~PFLAG_ONTRAIN;
8 years ago
m_iTrain = TRAIN_NEW | TRAIN_OFF;
9 years ago
return;
}
pev->velocity = g_vecZero;
vel = 0;
8 years ago
if( m_afButtonPressed & IN_FORWARD )
9 years ago
{
vel = 1;
pTrain->Use( this, this, USE_SET, (float)vel );
}
8 years ago
else if( m_afButtonPressed & IN_BACK )
9 years ago
{
vel = -1;
pTrain->Use( this, this, USE_SET, (float)vel );
}
8 years ago
if( vel )
9 years ago
{
8 years ago
m_iTrain = TrainSpeed( pTrain->pev->speed, pTrain->pev->impulse );
9 years ago
m_iTrain |= TRAIN_ACTIVE|TRAIN_NEW;
}
}
else if( m_iTrain & TRAIN_ACTIVE )
9 years ago
m_iTrain = TRAIN_NEW; // turn off train
8 years ago
if( pev->button & IN_JUMP )
9 years ago
{
// If on a ladder, jump off the ladder
// else Jump
Jump();
}
// If trying to duck, already ducked, or in the process of ducking
8 years ago
if( ( pev->button & IN_DUCK ) || FBitSet( pev->flags,FL_DUCKING ) || ( m_afPhysicsFlags & PFLAG_DUCKING ) )
9 years ago
Duck();
8 years ago
if( !FBitSet( pev->flags, FL_ONGROUND ) )
9 years ago
{
m_flFallVelocity = -pev->velocity.z;
}
8 years ago
// StudioFrameAdvance();//!!!HACKHACK!!! Can't be hit by traceline when not animating?
9 years ago
// Clear out ladder pointer
m_hEnemy = NULL;
8 years ago
if( m_afPhysicsFlags & PFLAG_ONBARNACLE )
9 years ago
{
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
8 years ago
#define DMG_POISON (1 << 16) // blood poisioning
9 years ago
#define DMG_RADIATION (1 << 17) // radiation exposure
#define DMG_DROWNRECOVER (1 << 18) // drown recovery
8 years ago
#define DMG_ACID (1 << 19) // toxic chemicals or acid burns
9 years ago
#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 8bit 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.
8 years ago
//#define PARALYZE_DURATION 30 // number of 2 second intervals to take damage
9 years ago
//#define PARALYZE_DAMAGE 0.0 // damage to take each 2 second interval
8 years ago
//#define NERVEGAS_DURATION 16
9 years ago
//#define NERVEGAS_DAMAGE 5.0
//#define POISON_DURATION 25
8 years ago
//#define POISON_DAMAGE 2.0
9 years ago
8 years ago
//#define RADIATION_DURATION 50
//#define RADIATION_DAMAGE 1.0
9 years ago
8 years ago
//#define ACID_DURATION 10
9 years ago
//#define ACID_DAMAGE 5.0
8 years ago
//#define SLOWBURN_DURATION 2
9 years ago
//#define SLOWBURN_DAMAGE 1.0
8 years ago
//#define SLOWFREEZE_DURATION 1.0
//#define SLOWFREEZE_DAMAGE 3.0
9 years ago
void CBasePlayer::CheckTimeBasedDamage()
{
int i;
BYTE bDuration = 0;
static float gtbdPrev = 0.0;
8 years ago
if( !( m_bitsDamageType & DMG_TIMEBASED ) )
9 years ago
return;
// only check for time based damage approx. every 2 seconds
8 years ago
if( fabs( gpGlobals->time - m_tbdPrev ) < 2.0 )
9 years ago
return;
9 years ago
m_tbdPrev = gpGlobals->time;
8 years ago
for( i = 0; i < CDMG_TIMEBASED; i++ )
9 years ago
{
// make sure bit is set for damage type
8 years ago
if( m_bitsDamageType & ( DMG_PARALYZE << i ) )
9 years ago
{
8 years ago
switch( i )
9 years ago
{
case itbd_Paralyze:
// UNDONE - flag movement as half-speed
bDuration = PARALYZE_DURATION;
break;
case itbd_NerveGas:
8 years ago
//TakeDamage( pev, pev, NERVEGAS_DAMAGE, DMG_GENERIC );
9 years ago
bDuration = NERVEGAS_DURATION;
break;
case itbd_Poison:
8 years ago
TakeDamage( pev, pev, POISON_DAMAGE, DMG_GENERIC );
9 years ago
bDuration = POISON_DURATION;
break;
case itbd_Radiation:
8 years ago
//TakeDamage( pev, pev, RADIATION_DAMAGE, DMG_GENERIC );
9 years ago
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
8 years ago
if( m_idrowndmg > m_idrownrestored )
9 years ago
{
8 years ago
int idif = min( m_idrowndmg - m_idrownrestored, 10 );
9 years ago
8 years ago
TakeHealth( idif, DMG_GENERIC );
9 years ago
m_idrownrestored += idif;
}
bDuration = 4; // get up to 5*10 = 50 points back
break;
case itbd_Acid:
8 years ago
//TakeDamage( pev, pev, ACID_DAMAGE, DMG_GENERIC );
9 years ago
bDuration = ACID_DURATION;
break;
case itbd_SlowBurn:
8 years ago
//TakeDamage( pev, pev, SLOWBURN_DAMAGE, DMG_GENERIC );
9 years ago
bDuration = SLOWBURN_DURATION;
break;
case itbd_SlowFreeze:
8 years ago
//TakeDamage( pev, pev, SLOWFREEZE_DAMAGE, DMG_GENERIC );
9 years ago
bDuration = SLOWFREEZE_DURATION;
break;
default:
bDuration = 0;
}
8 years ago
if( m_rgbTimeBasedDamage[i] )
9 years ago
{
// use up an antitoxin on poison or nervegas after a few seconds of damage
8 years ago
if( ( ( i == itbd_NerveGas ) && ( m_rgbTimeBasedDamage[i] < NERVEGAS_DURATION ) ) ||
( ( i == itbd_Poison ) && ( m_rgbTimeBasedDamage[i] < POISON_DURATION ) ) )
9 years ago
{
8 years ago
if( m_rgItems[ITEM_ANTIDOTE] )
9 years ago
{
m_rgbTimeBasedDamage[i] = 0;
m_rgItems[ITEM_ANTIDOTE]--;
8 years ago
SetSuitUpdate( "!HEV_HEAL4", FALSE, SUIT_REPEAT_OK );
9 years ago
}
}
// decrement damage duration, detect when done.
8 years ago
if( !m_rgbTimeBasedDamage[i] || --m_rgbTimeBasedDamage[i] == 0 )
9 years ago
{
m_rgbTimeBasedDamage[i] = 0;
9 years ago
// if we're done, clear damage bits
8 years ago
m_bitsDamageType &= ~( DMG_PARALYZE << i );
9 years ago
}
}
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 (1st 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
8 years ago
void CBasePlayer::UpdateGeigerCounter( void )
9 years ago
{
BYTE range;
// delay per update ie: don't flood net with these msgs
8 years ago
if( gpGlobals->time < m_flgeigerDelay )
9 years ago
return;
m_flgeigerDelay = gpGlobals->time + GEIGERDELAY;
// send range to radition source to client
8 years ago
range = (BYTE)( m_flgeigerRange / 4 );
9 years ago
8 years ago
if( range != m_igeigerRangePrev )
9 years ago
{
m_igeigerRangePrev = range;
MESSAGE_BEGIN( MSG_ONE, gmsgGeigerRange, NULL, pev );
WRITE_BYTE( range );
MESSAGE_END();
}
// reset counter and semaphore
8 years ago
if( !RANDOM_LONG( 0, 3 ) )
9 years ago
m_flgeigerRange = 1000;
}
/*
================
CheckSuitUpdate
Play suit update if it's time
================
*/
8 years ago
#define SUITUPDATETIME 3.5
#define SUITFIRSTUPDATETIME 0.1
9 years ago
void CBasePlayer::CheckSuitUpdate()
{
int i;
int isentence = 0;
int isearch = m_iSuitPlayNext;
9 years ago
// Ignore suit updates if no suit
8 years ago
if( !( pev->weapons & ( 1 << WEAPON_SUIT ) ) )
9 years ago
return;
// if in range of radiation source, ping geiger counter
UpdateGeigerCounter();
8 years ago
if( g_pGameRules->IsMultiplayer() )
9 years ago
{
// don't bother updating HEV voice in multiplayer.
return;
}
8 years ago
if( gpGlobals->time >= m_flSuitUpdate && m_flSuitUpdate > 0 )
9 years ago
{
// play a sentence off of the end of the queue
8 years ago
for( i = 0; i < CSUITPLAYLIST; i++ )
{
8 years ago
if( ( isentence = m_rgSuitPlayList[isearch] ) )
9 years ago
break;
8 years ago
if( ++isearch == CSUITPLAYLIST )
9 years ago
isearch = 0;
}
9 years ago
8 years ago
if( isentence )
9 years ago
{
m_rgSuitPlayList[isearch] = 0;
8 years ago
if( isentence > 0 )
9 years ago
{
// play sentence number
char sentence[CBSENTENCENAME_MAX + 1];
8 years ago
strcpy( sentence, "!" );
strcat( sentence, gszallsentencenames[isentence] );
EMIT_SOUND_SUIT( ENT( pev ), sentence );
9 years ago
}
else
{
// play sentence group
8 years ago
EMIT_GROUPID_SUIT( ENT( pev ), -isentence );
9 years ago
}
m_flSuitUpdate = gpGlobals->time + SUITUPDATETIME;
9 years ago
}
else
// queue is empty, don't check
m_flSuitUpdate = 0;
}
}
9 years ago
// 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.
8 years ago
void CBasePlayer::SetSuitUpdate( char *name, int fgroup, int iNoRepeatTime )
9 years ago
{
int i;
int isentence;
int iempty = -1;
9 years ago
// Ignore suit updates if no suit
8 years ago
if( !( pev->weapons & ( 1 << WEAPON_SUIT ) ) )
9 years ago
return;
8 years ago
if( g_pGameRules->IsMultiplayer() )
9 years ago
{
// 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
8 years ago
if( !name )
9 years ago
{
8 years ago
for( i = 0; i < CSUITPLAYLIST; i++ )
9 years ago
m_rgSuitPlayList[i] = 0;
return;
}
9 years ago
// get sentence or group number
8 years ago
if( !fgroup )
9 years ago
{
8 years ago
isentence = SENTENCEG_Lookup( name, NULL );
if( isentence < 0 )
9 years ago
return;
}
else
// mark group number as negative
8 years ago
isentence = -SENTENCEG_GetIndex( name );
9 years ago
// check norepeat list - this list lets us cancel
// the playback of words or sentences that have already
// been played within a certain time.
8 years ago
for( i = 0; i < CSUITNOREPEAT; i++ )
9 years ago
{
8 years ago
if( isentence == m_rgiSuitNoRepeat[i] )
{
9 years ago
// this sentence or group is already in
// the norepeat list
8 years ago
if( m_rgflSuitNoRepeatTime[i] < gpGlobals->time )
{
9 years ago
// norepeat time has expired, clear it out
m_rgiSuitNoRepeat[i] = 0;
m_rgflSuitNoRepeatTime[i] = 0.0;
iempty = i;
break;
}
9 years ago
else
{
9 years ago
// don't play, still marked as norepeat
return;
}
}
9 years ago
// keep track of empty slot
8 years ago
if( !m_rgiSuitNoRepeat[i] )
9 years ago
iempty = i;
}
// sentence is not in norepeat list, save if norepeat time was given
8 years ago
if( iNoRepeatTime )
9 years ago
{
8 years ago
if( iempty < 0 )
iempty = RANDOM_LONG( 0, CSUITNOREPEAT - 1 ); // pick random slot to take over
9 years ago
m_rgiSuitNoRepeat[iempty] = isentence;
m_rgflSuitNoRepeatTime[iempty] = iNoRepeatTime + gpGlobals->time;
}
// find empty spot in queue, or overwrite last spot
m_rgSuitPlayList[m_iSuitPlayNext++] = isentence;
8 years ago
if( m_iSuitPlayNext == CSUITPLAYLIST )
9 years ago
m_iSuitPlayNext = 0;
8 years ago
if( m_flSuitUpdate <= gpGlobals->time )
9 years ago
{
8 years ago
if( m_flSuitUpdate == 0 )
9 years ago
// 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
================
*/
8 years ago
static void CheckPowerups( entvars_t *pev )
9 years ago
{
8 years ago
if( pev->health <= 0 )
9 years ago
return;
pev->modelindex = g_ulModelIndexPlayer; // don't use eyes
}
//=========================================================
// UpdatePlayerSound - updates the position of the player's
// reserved sound slot in the sound list.
//=========================================================
8 years ago
void CBasePlayer::UpdatePlayerSound( void )
9 years ago
{
int iBodyVolume;
int iVolume;
CSound *pSound;
8 years ago
pSound = CSoundEnt::SoundPointerForIndex( CSoundEnt::ClientSoundIndex( edict() ) );
9 years ago
8 years ago
if( !pSound )
9 years ago
{
8 years ago
ALERT( at_console, "Client lost reserved sound!\n" );
9 years ago
return;
}
pSound->m_iType = bits_SOUND_NONE;
// now calculate the best target volume for the sound. If the player's weapon
// is louder than his body/movement, use the weapon volume, else, use the body volume.
8 years ago
if( FBitSet( pev->flags, FL_ONGROUND ) )
{
9 years ago
iBodyVolume = pev->velocity.Length();
// clamp the noise that can be made by the body, in case a push trigger,
// weapon recoil, or anything shoves the player abnormally fast.
8 years ago
if( iBodyVolume > 512 )
9 years ago
{
iBodyVolume = 512;
}
}
else
{
iBodyVolume = 0;
}
8 years ago
if( pev->button & IN_JUMP )
9 years ago
{
iBodyVolume += 100;
}
// convert player move speed and actions into sound audible by monsters.
8 years ago
if( m_iWeaponVolume > iBodyVolume )
9 years ago
{
m_iTargetVolume = m_iWeaponVolume;
// OR in the bits for COMBAT sound if the weapon is being louder than the player.
pSound->m_iType |= bits_SOUND_COMBAT;
}
else
{
m_iTargetVolume = iBodyVolume;
}
// decay weapon volume over time so bits_SOUND_COMBAT stays set for a while
m_iWeaponVolume -= 250 * gpGlobals->frametime;
8 years ago
if( m_iWeaponVolume < 0 )
9 years ago
{
iVolume = 0;
}
// if target volume is greater than the player sound's current volume, we paste the new volume in
// immediately. If target is less than the current volume, current volume is not set immediately to the
// lower volume, rather works itself towards target volume over time. This gives monsters a much better chance
// to hear a sound, especially if they don't listen every frame.
iVolume = pSound->m_iVolume;
8 years ago
if( m_iTargetVolume > iVolume )
9 years ago
{
iVolume = m_iTargetVolume;
}
8 years ago
else if( iVolume > m_iTargetVolume )
9 years ago
{
iVolume -= 250 * gpGlobals->frametime;
8 years ago
if( iVolume < m_iTargetVolume )
9 years ago
{
iVolume = 0;
}
}
8 years ago
if( m_fNoPlayerSound )
9 years ago
{
// debugging flag, lets players move around and shoot without monsters hearing.
iVolume = 0;
}
8 years ago
if( gpGlobals->time > m_flStopExtraSoundTime )
9 years ago
{
// since the extra sound that a weapon emits only lasts for one client frame, we keep that sound around for a server frame or two
// after actual emission to make sure it gets heard.
m_iExtraSoundTypes = 0;
}
8 years ago
if( pSound )
9 years ago
{
pSound->m_vecOrigin = pev->origin;
pSound->m_iType |= ( bits_SOUND_PLAYER | m_iExtraSoundTypes );
pSound->m_iVolume = iVolume;
}
// keep track of virtual muzzle flash
m_iWeaponFlash -= 256 * gpGlobals->frametime;
8 years ago
if( m_iWeaponFlash < 0 )
9 years ago
m_iWeaponFlash = 0;
8 years ago
//UTIL_MakeVectors( pev->angles );
9 years ago
//gpGlobals->v_forward.z = 0;
// Below are a couple of useful little bits that make it easier to determine just how much noise the
// player is making.
8 years ago
// UTIL_ParticleEffect( pev->origin + gpGlobals->v_forward * iVolume, g_vecZero, 255, 25 );
//ALERT( at_console, "%d/%d\n", iVolume, m_iTargetVolume );
9 years ago
}
void CBasePlayer::PostThink()
{
8 years ago
if( g_fGameOver )
goto pt_end; // intermission or finale
9 years ago
8 years ago
if( !IsAlive() )
9 years ago
goto pt_end;
// Handle Tank controlling
8 years ago
if( m_pTank != NULL )
{
// if they've moved too far from the gun, or selected a weapon, unuse the gun
8 years ago
if( m_pTank->OnControls( pev ) && !pev->weaponmodel )
9 years ago
{
m_pTank->Use( this, this, USE_SET, 2 ); // try fire the gun
}
else
{
// they've moved off the platform
9 years ago
m_pTank->Use( this, this, USE_OFF, 0 );
m_pTank = NULL;
}
}
// do weapon stuff
8 years ago
ItemPostFrame();
9 years ago
8 years ago
// 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
9 years ago
8 years ago
if( ( FBitSet( pev->flags, FL_ONGROUND ) ) && ( pev->health > 0 ) && m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHHOLD )
9 years ago
{
8 years ago
// ALERT( at_console, "%f\n", m_flFallVelocity );
if( pev->watertype == CONTENT_WATER )
9 years ago
{
// 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
8 years ago
// if( !pev->groundentity || VARS(pev->groundentity )->velocity.z == 0 )
// EMIT_SOUND( ENT( pev ), CHAN_BODY, "player/pl_wade1.wav", 1, ATTN_NORM );
9 years ago
}
8 years ago
else if( m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED )
{
// after this point, we start doing damage
9 years ago
float flFallDamage = g_pGameRules->FlPlayerFallDamage( this );
8 years ago
if( flFallDamage > pev->health )
{
//splat
9 years ago
// note: play on item channel because we play footstep landing on body channel
8 years ago
EMIT_SOUND( ENT( pev ), CHAN_ITEM, "common/bodysplat.wav", 1, ATTN_NORM );
9 years ago
}
8 years ago
if( flFallDamage > 0 )
9 years ago
{
8 years ago
TakeDamage( VARS( eoNullEntity ), VARS( eoNullEntity ), flFallDamage, DMG_FALL );
9 years ago
pev->punchangle.x = 0;
}
}
8 years ago
if( IsAlive() )
9 years ago
{
SetAnimation( PLAYER_WALK );
}
}
9 years ago
8 years ago
if( FBitSet( pev->flags, FL_ONGROUND ) )
{
if( m_flFallVelocity > 64 && !g_pGameRules->IsMultiplayer() )
9 years ago
{
8 years ago
CSoundEnt::InsertSound( bits_SOUND_PLAYER, pev->origin, m_flFallVelocity, 0.2 );
9 years ago
// ALERT( at_console, "fall %f\n", m_flFallVelocity );
}
m_flFallVelocity = 0;
}
// select the proper animation for the player character
8 years ago
if( IsAlive() )
9 years ago
{
8 years ago
if( !pev->velocity.x && !pev->velocity.y )
9 years ago
SetAnimation( PLAYER_IDLE );
8 years ago
else if( ( pev->velocity.x || pev->velocity.y ) && ( FBitSet( pev->flags, FL_ONGROUND ) ) )
9 years ago
SetAnimation( PLAYER_WALK );
8 years ago
else if( pev->waterlevel > 1 )
9 years ago
SetAnimation( PLAYER_WALK );
}
8 years ago
StudioFrameAdvance();
CheckPowerups( pev );
9 years ago
UpdatePlayerSound();
// Track button info so we can detect 'pressed' and 'released' buttons next frame
m_afButtonLast = pev->button;
pt_end:
#if defined( CLIENT_WEAPONS )
// Decay timers on weapons
9 years ago
// go through all of the weapons and make a list of the ones to pack
8 years ago
for( int i = 0; i < MAX_ITEM_TYPES; i++ )
9 years ago
{
8 years ago
if( m_rgpPlayerItems[i] )
9 years ago
{
8 years ago
CBasePlayerItem *pPlayerItem = m_rgpPlayerItems[i];
9 years ago
8 years ago
while( pPlayerItem )
9 years ago
{
CBasePlayerWeapon *gun;
gun = (CBasePlayerWeapon *)pPlayerItem->GetWeaponPtr();
8 years ago
if( gun && gun->UseDecrement() )
9 years ago
{
8 years ago
gun->m_flNextPrimaryAttack = max( gun->m_flNextPrimaryAttack - gpGlobals->frametime, -1.0 );
gun->m_flNextSecondaryAttack = max( gun->m_flNextSecondaryAttack - gpGlobals->frametime, -0.001 );
9 years ago
8 years ago
if( gun->m_flTimeWeaponIdle != 1000 )
9 years ago
{
8 years ago
gun->m_flTimeWeaponIdle = max( gun->m_flTimeWeaponIdle - gpGlobals->frametime, -0.001 );
9 years ago
}
8 years ago
if( gun->pev->fuser1 != 1000 )
9 years ago
{
8 years ago
gun->pev->fuser1 = max( gun->pev->fuser1 - gpGlobals->frametime, -0.001 );
9 years ago
}
// Only decrement if not flagged as NO_DECREMENT
8 years ago
/*if( gun->m_flPumpTime != 1000 )
{
8 years ago
gun->m_flPumpTime = max( gun->m_flPumpTime - gpGlobals->frametime, -0.001 );
}*/
9 years ago
}
pPlayerItem = pPlayerItem->m_pNext;
}
}
}
m_flNextAttack -= gpGlobals->frametime;
8 years ago
if( m_flNextAttack < -0.001 )
9 years ago
m_flNextAttack = -0.001;
8 years ago
if( m_flNextAmmoBurn != 1000 )
9 years ago
{
m_flNextAmmoBurn -= gpGlobals->frametime;
8 years ago
if( m_flNextAmmoBurn < -0.001 )
9 years ago
m_flNextAmmoBurn = -0.001;
}
8 years ago
if( m_flAmmoStartCharge != 1000 )
9 years ago
{
m_flAmmoStartCharge -= gpGlobals->frametime;
8 years ago
if( m_flAmmoStartCharge < -0.001 )
9 years ago
m_flAmmoStartCharge = -0.001;
}
#else
return;
#endif
}
// checks if the spot is clear of players
BOOL IsSpawnPointValid( CBaseEntity *pPlayer, CBaseEntity *pSpot )
{
CBaseEntity *ent = NULL;
8 years ago
if( !pSpot->IsTriggered( pPlayer ) )
9 years ago
{
return FALSE;
}
8 years ago
while( ( ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 ) ) != NULL )
9 years ago
{
// if ent is a client, don't spawn on 'em
8 years ago
if( ent->IsPlayer() && ent != pPlayer )
9 years ago
return FALSE;
}
return TRUE;
}
DLL_GLOBAL CBaseEntity *g_pLastSpawn;
8 years ago
inline int FNullEnt( CBaseEntity *ent ) { return ( ent == NULL ) || FNullEnt( ent->edict() ); }
9 years ago
/*
============
EntSelectSpawnPoint
Returns the entity to spawn at
USES AND SETS GLOBAL g_pLastSpawn
============
*/
edict_t *EntSelectSpawnPoint( CBaseEntity *pPlayer )
{
CBaseEntity *pSpot;
8 years ago
edict_t *player;
9 years ago
player = pPlayer->edict();
// choose a info_player_deathmatch point
8 years ago
if( g_pGameRules->IsCoOp() )
9 years ago
{
8 years ago
pSpot = UTIL_FindEntityByClassname( g_pLastSpawn, "info_player_coop" );
if( !FNullEnt( pSpot ) )
9 years ago
goto ReturnSpot;
8 years ago
pSpot = UTIL_FindEntityByClassname( g_pLastSpawn, "info_player_start" );
if( !FNullEnt(pSpot) )
9 years ago
goto ReturnSpot;
}
8 years ago
else if( g_pGameRules->IsDeathmatch() )
9 years ago
{
pSpot = g_pLastSpawn;
// Randomize the start spot
8 years ago
for( int i = RANDOM_LONG( 1, 5 ); i > 0; i-- )
9 years ago
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
8 years ago
if( FNullEnt( pSpot ) ) // skip over the null point
9 years ago
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
CBaseEntity *pFirstSpot = pSpot;
do
{
8 years ago
if( pSpot )
9 years ago
{
// check if pSpot is valid
8 years ago
if( IsSpawnPointValid( pPlayer, pSpot ) )
9 years ago
{
8 years ago
if( pSpot->pev->origin == Vector( 0, 0, 0 ) )
9 years ago
{
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
continue;
}
// if so, go to pSpot
goto ReturnSpot;
}
}
// increment pSpot
pSpot = UTIL_FindEntityByClassname( pSpot, "info_player_deathmatch" );
8 years ago
} while( pSpot != pFirstSpot ); // loop if we're not back to the start
9 years ago
// we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there
8 years ago
if( !FNullEnt( pSpot ) )
9 years ago
{
CBaseEntity *ent = NULL;
8 years ago
while( ( ent = UTIL_FindEntityInSphere( ent, pSpot->pev->origin, 128 ) ) != NULL )
9 years ago
{
// if ent is a client, kill em (unless they are ourselves)
8 years ago
if( ent->IsPlayer() && !(ent->edict() == player) )
ent->TakeDamage( VARS( INDEXENT( 0 ) ), VARS( INDEXENT( 0 ) ), 300, DMG_GENERIC );
9 years ago
}
goto ReturnSpot;
}
}
// If startspot is set, (re)spawn there.
8 years ago
if( FStringNull( gpGlobals->startspot ) || !strlen(STRING( gpGlobals->startspot ) ) )
9 years ago
{
8 years ago
pSpot = UTIL_FindEntityByClassname( NULL, "info_player_start" );
if( !FNullEnt( pSpot ) )
9 years ago
goto ReturnSpot;
}
else
{
8 years ago
pSpot = UTIL_FindEntityByTargetname( NULL, STRING( gpGlobals->startspot ) );
if( !FNullEnt( pSpot ) )
9 years ago
goto ReturnSpot;
}
ReturnSpot:
8 years ago
if( FNullEnt( pSpot ) )
9 years ago
{
8 years ago
ALERT( at_error, "PutClientInServer: no info_player_start on level" );
return INDEXENT( 0 );
9 years ago
}
g_pLastSpawn = pSpot;
return pSpot->edict();
}
void CBasePlayer::Spawn( void )
{
8 years ago
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.
9 years ago
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" );
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" );
8 years ago
pev->fov = m_iFOV = 0;// init field of view.
m_iClientFOV = -1; // make sure fov reset is sent
9 years ago
8 years ago
m_flNextDecalTime = 0;// let this player decal as soon as he spawns.
9 years ago
m_flgeigerDelay = gpGlobals->time + 2.0; // wait a few seconds until user-defined message registrations
// are recieved by all clients
8 years ago
m_flTimeStepSound = 0;
9 years ago
m_iStepLeft = 0;
8 years ago
m_flFieldOfView = 0.5;// some monsters use this to determine whether or not the player is looking at them.
9 years ago
8 years ago
m_bloodColor = BLOOD_COLOR_RED;
m_flNextAttack = UTIL_WeaponTimeBase();
9 years ago
StartSneaking();
m_iFlashBattery = 99;
m_flFlashLightTime = 1; // force first message
// dont let uninitialized value here hurt the player
9 years ago
m_flFallVelocity = 0;
g_pGameRules->SetDefaultPlayerTeam( this );
g_pGameRules->GetPlayerSpawnSpot( this );
8 years ago
SET_MODEL( ENT( pev ), "models/player.mdl" );
g_ulModelIndexPlayer = pev->modelindex;
8 years ago
pev->sequence = LookupActivity( ACT_IDLE );
9 years ago
8 years ago
if( FBitSet( pev->flags, FL_DUCKING ) )
UTIL_SetSize( pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
9 years ago
else
8 years ago
UTIL_SetSize( pev, VEC_HULL_MIN, VEC_HULL_MAX );
9 years ago
pev->view_ofs = VEC_VIEW;
9 years ago
Precache();
8 years ago
m_HackedGunPos = Vector( 0, 32, 0 );
9 years ago
8 years ago
if( m_iPlayerSound == SOUNDLIST_EMPTY )
9 years ago
{
8 years ago
ALERT( at_console, "Couldn't alloc player sound slot!\n" );
9 years ago
}
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;
// reset all ammo values to 0
8 years ago
for( int i = 0; i < MAX_AMMO_SLOTS; i++ )
9 years ago
{
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;
8 years ago
9 years ago
m_flNextChatTime = gpGlobals->time;
g_pGameRules->PlayerSpawn( this );
}
8 years ago
void CBasePlayer::Precache( void )
9 years ago
{
// 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.
8 years ago
9 years ago
// !!!BUGBUG - now that we have multiplayer, this needs to be moved!
8 years ago
if( WorldGraph.m_fGraphPresent && !WorldGraph.m_fGraphPointersSet )
9 years ago
{
8 years ago
if( !WorldGraph.FSetGraphPointers() )
9 years ago
{
8 years ago
ALERT( at_console, "**Graph pointers were not set!\n" );
9 years ago
}
else
{
8 years ago
ALERT( at_console, "**Graph Pointers Set!\n" );
}
9 years ago
}
// SOUNDS / MODELS ARE PRECACHED in ClientPrecache() (game specific)
// because they need to precache before any clients have connected
// 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
8 years ago
if( gInitHUD )
9 years ago
m_fInitHUD = TRUE;
}
int CBasePlayer::Save( CSave &save )
{
8 years ago
if( !CBaseMonster::Save( save ) )
9 years ago
return 0;
8 years ago
return save.WriteFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE( m_playerSaveData ) );
9 years ago
}
//
// Marks everything as new so the player will resend this to the hud.
//
8 years ago
void CBasePlayer::RenewItems( void )
9 years ago
{
}
int CBasePlayer::Restore( CRestore &restore )
{
8 years ago
if( !CBaseMonster::Restore( restore ) )
9 years ago
return 0;
8 years ago
int status = restore.ReadFields( "PLAYER", this, m_playerSaveData, ARRAYSIZE( m_playerSaveData ) );
9 years ago
SAVERESTOREDATA *pSaveData = (SAVERESTOREDATA *)gpGlobals->pSaveData;
// landmark isn't present.
8 years ago
if( !pSaveData->fUseLandmark )
9 years ago
{
ALERT( at_console, "No Landmark:%s\n", pSaveData->szLandmarkName );
// default to normal spawn
8 years ago
edict_t *pentSpawnSpot = EntSelectSpawnPoint( this );
pev->origin = VARS( pentSpawnSpot )->origin + Vector( 0, 0, 1 );
pev->angles = VARS( pentSpawnSpot )->angles;
9 years ago
}
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
8 years ago
m_bloodColor = BLOOD_COLOR_RED;
9 years ago
g_ulModelIndexPlayer = pev->modelindex;
9 years ago
8 years ago
if( FBitSet( pev->flags, FL_DUCKING ) )
9 years ago
{
// Use the crouch HACK
//FixPlayerCrouchStuck( edict() );
// Don't need to do this with new player prediction code.
8 years ago
UTIL_SetSize( pev, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
9 years ago
}
else
{
8 years ago
UTIL_SetSize( pev, VEC_HULL_MIN, VEC_HULL_MAX );
9 years ago
}
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "hl", "1" );
8 years ago
if( m_fLongJump )
9 years ago
{
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "1" );
}
else
{
g_engfuncs.pfnSetPhysicsKeyValue( edict(), "slj", "0" );
}
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;
8 years ago
pItem = m_rgpPlayerItems[iItem];
if( !pItem )
9 years ago
return;
8 years ago
if( pItem == m_pActiveItem )
9 years ago
{
// select the next one in the chain
pItem = m_pActiveItem->m_pNext;
8 years ago
if( !pItem )
9 years ago
{
return;
}
CBasePlayerItem *pLast;
pLast = pItem;
8 years ago
while( pLast->m_pNext )
9 years ago
pLast = pLast->m_pNext;
// relink chain
pLast->m_pNext = m_pActiveItem;
m_pActiveItem->m_pNext = NULL;
8 years ago
m_rgpPlayerItems[iItem] = pItem;
9 years ago
}
8 years ago
ResetAutoaim();
9 years ago
// FIX, this needs to queue them up and delay
8 years ago
if( m_pActiveItem )
9 years ago
{
8 years ago
m_pActiveItem->Holster();
9 years ago
}
8 years ago
9 years ago
m_pActiveItem = pItem;
8 years ago
if( m_pActiveItem )
9 years ago
{
8 years ago
m_pActiveItem->Deploy();
m_pActiveItem->UpdateItemInfo();
9 years ago
}
}
8 years ago
void CBasePlayer::SelectItem( const char *pstr )
9 years ago
{
8 years ago
if( !pstr )
9 years ago
return;
CBasePlayerItem *pItem = NULL;
8 years ago
for( int i = 0; i < MAX_ITEM_TYPES; i++ )
9 years ago
{
8 years ago
if( m_rgpPlayerItems[i] )
9 years ago
{
pItem = m_rgpPlayerItems[i];
8 years ago
while( pItem )
9 years ago
{
8 years ago
if( FClassnameIs( pItem->pev, pstr ) )
9 years ago
break;
pItem = pItem->m_pNext;
}
}
8 years ago
if( pItem )
9 years ago
break;
}
8 years ago
if( !pItem )
9 years ago
return;
8 years ago
if( pItem == m_pActiveItem )
9 years ago
return;
8 years ago
ResetAutoaim();
9 years ago
// FIX, this needs to queue them up and delay
8 years ago
if( m_pActiveItem )
m_pActiveItem->Holster();
9 years ago
m_pLastItem = m_pActiveItem;
m_pActiveItem = pItem;
8 years ago
if( m_pActiveItem )
9 years ago
{
8 years ago
m_pActiveItem->Deploy();
m_pActiveItem->UpdateItemInfo();
9 years ago
}
}
8 years ago
void CBasePlayer::SelectLastItem( void )
9 years ago
{
8 years ago
if( !m_pLastItem )
9 years ago
{
return;
}
8 years ago
if( m_pActiveItem && !m_pActiveItem->CanHolster() )
9 years ago
{
return;
}
8 years ago
ResetAutoaim();
9 years ago
// FIX, this needs to queue them up and delay
8 years ago
if( m_pActiveItem )
m_pActiveItem->Holster();
9 years ago
CBasePlayerItem *pTemp = m_pActiveItem;
m_pActiveItem = m_pLastItem;
m_pLastItem = pTemp;
8 years ago
m_pActiveItem->Deploy();
m_pActiveItem->UpdateItemInfo();
9 years ago
}
//==============================================
// HasWeapons - do I have any weapons at all?
//==============================================
BOOL CBasePlayer::HasWeapons( void )
{
int i;
8 years ago
for( i = 0; i < MAX_ITEM_TYPES; i++ )
9 years ago
{
8 years ago
if( m_rgpPlayerItems[i] )
9 years ago
{
return TRUE;
}
}
return FALSE;
}
void CBasePlayer::SelectPrevItem( int iItem )
{
}
const char *CBasePlayer::TeamID( void )
{
8 years ago
if( pev == NULL ) // Not fully connected yet
9 years ago
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:
8 years ago
void Spawn( entvars_t *pevOwner );
void Think( void );
9 years ago
8 years ago
virtual int ObjectCaps( void ) { return FCAP_DONT_SAVE; }
9 years ago
};
8 years ago
void CSprayCan::Spawn( entvars_t *pevOwner )
9 years ago
{
8 years ago
pev->origin = pevOwner->origin + Vector( 0, 0, 32 );
9 years ago
pev->angles = pevOwner->v_angle;
8 years ago
pev->owner = ENT( pevOwner );
9 years ago
pev->frame = 0;
pev->nextthink = gpGlobals->time + 0.1;
8 years ago
EMIT_SOUND( ENT( pev ), CHAN_VOICE, "player/sprayer.wav", 1, ATTN_NORM );
9 years ago
}
void CSprayCan::Think( void )
{
8 years ago
TraceResult tr;
9 years ago
int playernum;
int nFrames;
CBasePlayer *pPlayer;
8 years ago
pPlayer = (CBasePlayer *)GET_PRIVATE( pev->owner );
if( pPlayer )
9 years ago
nFrames = pPlayer->GetCustomDecalFrames();
else
nFrames = -1;
8 years ago
playernum = ENTINDEX( pev->owner );
// ALERT( at_console, "Spray by player %i, %i of %i\n", playernum, (int)( pev->frame + 1 ), nFrames );
9 years ago
8 years ago
UTIL_MakeVectors( pev->angles );
UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr );
9 years ago
// No customization present.
8 years ago
if( nFrames == -1 )
9 years ago
{
UTIL_DecalTrace( &tr, DECAL_LAMBDA6 );
UTIL_Remove( this );
}
else
{
UTIL_PlayerDecalTrace( &tr, playernum, pev->frame, TRUE );
// Just painted last custom frame.
8 years ago
if( pev->frame++ >= ( nFrames - 1 ) )
9 years ago
UTIL_Remove( this );
}
pev->nextthink = gpGlobals->time + 0.1;
}
8 years ago
class CBloodSplat : public CBaseEntity
9 years ago
{
public:
8 years ago
void Spawn( entvars_t *pevOwner );
void Spray( void );
9 years ago
};
8 years ago
void CBloodSplat::Spawn( entvars_t *pevOwner )
9 years ago
{
8 years ago
pev->origin = pevOwner->origin + Vector( 0, 0, 32 );
9 years ago
pev->angles = pevOwner->v_angle;
8 years ago
pev->owner = ENT( pevOwner );
9 years ago
SetThink( &CBloodSplat::Spray );
pev->nextthink = gpGlobals->time + 0.1;
}
8 years ago
void CBloodSplat::Spray( void )
9 years ago
{
8 years ago
TraceResult tr;
8 years ago
if( g_Language != LANGUAGE_GERMAN )
9 years ago
{
8 years ago
UTIL_MakeVectors( pev->angles );
UTIL_TraceLine( pev->origin, pev->origin + gpGlobals->v_forward * 128, ignore_monsters, pev->owner, & tr );
9 years ago
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;
8 years ago
int istr = MAKE_STRING( pszName );
9 years ago
8 years ago
pent = CREATE_NAMED_ENTITY( istr );
if( FNullEnt( pent ) )
9 years ago
{
8 years ago
ALERT( at_console, "NULL Ent in GiveNamedItem!\n" );
9 years ago
return;
}
VARS( pent )->origin = pev->origin;
pent->v.spawnflags |= SF_NORESPAWN;
DispatchSpawn( pent );
DispatchTouch( pent, ENT( pev ) );
}
CBaseEntity *FindEntityForward( CBaseEntity *pMe )
{
TraceResult tr;
8 years ago
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 ) )
9 years ago
{
CBaseEntity *pHit = CBaseEntity::Instance( tr.pHit );
return pHit;
}
return NULL;
}
8 years ago
BOOL CBasePlayer::FlashlightIsOn( void )
9 years ago
{
8 years ago
return FBitSet( pev->effects, EF_DIMLIGHT );
9 years ago
}
8 years ago
void CBasePlayer::FlashlightTurnOn( void )
9 years ago
{
8 years ago
if( !g_pGameRules->FAllowFlashlight() )
9 years ago
{
return;
}
8 years ago
if( (pev->weapons & ( 1 << WEAPON_SUIT ) ) )
9 years ago
{
8 years ago
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, SOUND_FLASHLIGHT_ON, 1.0, ATTN_NORM, 0, PITCH_NORM );
SetBits( pev->effects, EF_DIMLIGHT );
9 years ago
MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev );
8 years ago
WRITE_BYTE( 1 );
WRITE_BYTE( m_iFlashBattery );
9 years ago
MESSAGE_END();
m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time;
}
}
8 years ago
void CBasePlayer::FlashlightTurnOff( void )
9 years ago
{
8 years ago
EMIT_SOUND_DYN( ENT( pev ), CHAN_WEAPON, SOUND_FLASHLIGHT_OFF, 1.0, ATTN_NORM, 0, PITCH_NORM );
ClearBits( pev->effects, EF_DIMLIGHT );
9 years ago
MESSAGE_BEGIN( MSG_ONE, gmsgFlashlight, NULL, pev );
8 years ago
WRITE_BYTE( 0 );
WRITE_BYTE( m_iFlashBattery );
9 years ago
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.
===============
*/
8 years ago
void CBasePlayer::ForceClientDllUpdate( void )
9 years ago
{
8 years ago
m_iClientHealth = -1;
9 years ago
m_iClientBattery = -1;
m_iTrain |= TRAIN_NEW; // Force new train message.
m_fWeapon = FALSE; // Force weapon send
m_fKnownItem = FALSE; // Force weaponinit messages.
m_fInitHUD = TRUE; // Force HUD gmsgResetHUD message
// Now force all the necessary messages
// to be sent.
UpdateClientData();
}
/*
============
ImpulseCommands
============
*/
extern float g_flWeaponCheat;
8 years ago
void CBasePlayer::ImpulseCommands()
9 years ago
{
8 years ago
TraceResult tr;// UNDONE: kill me! This is temporary for PreAlpha CDs
9 years ago
// Handle use events
PlayerUse();
9 years ago
int iImpulse = (int)pev->impulse;
8 years ago
switch( iImpulse )
9 years ago
{
case 99:
int iOn;
8 years ago
if( !gmsgLogo )
9 years ago
{
iOn = 1;
8 years ago
gmsgLogo = REG_USER_MSG( "Logo", 1 );
9 years ago
}
else
{
iOn = 0;
}
9 years ago
ASSERT( gmsgLogo > 0 );
9 years ago
// send "health" update message
MESSAGE_BEGIN( MSG_ONE, gmsgLogo, NULL, pev );
8 years ago
WRITE_BYTE( iOn );
9 years ago
MESSAGE_END();
if(!iOn)
gmsgLogo = 0;
break;
case 100:
// temporary flashlight for level designers
8 years ago
if( FlashlightIsOn() )
9 years ago
{
FlashlightTurnOff();
}
8 years ago
else
9 years ago
{
FlashlightTurnOn();
}
break;
case 201:
// paint decal
8 years ago
if( gpGlobals->time < m_flNextDecalTime )
9 years ago
{
// too early!
break;
}
8 years ago
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 );
9 years ago
8 years ago
if( tr.flFraction != 1.0 )
{
// line hit something, so paint a decal
9 years ago
m_flNextDecalTime = gpGlobals->time + decalfrequency.value;
8 years ago
CSprayCan *pCan = GetClassPtr( (CSprayCan *)NULL );
9 years ago
pCan->Spawn( pev );
}
break;
default:
// check all of the cheat impulse commands now
CheatImpulseCommands( iImpulse );
break;
}
9 years ago
pev->impulse = 0;
}
//=========================================================
//=========================================================
void CBasePlayer::CheatImpulseCommands( int iImpulse )
{
#if !defined( HLDEMO_BUILD )
8 years ago
if( g_flWeaponCheat == 0.0 )
9 years ago
{
return;
}
CBaseEntity *pEntity;
TraceResult tr;
8 years ago
switch( iImpulse )
9 years ago
{
case 76:
8 years ago
if( !giPrecacheGrunt )
9 years ago
{
8 years ago
giPrecacheGrunt = 1;
ALERT( at_console, "You must now restart to use Grunt-o-matic.\n" );
9 years ago
}
8 years ago
else
{
UTIL_MakeVectors( Vector( 0, pev->v_angle.y, 0 ) );
Create( "monster_human_grunt", pev->origin + gpGlobals->v_forward * 128, pev->angles );
}
break;
9 years ago
case 101:
gEvilImpulse101 = TRUE;
GiveNamedItem( "item_suit" );
GiveNamedItem( "item_battery" );
GiveNamedItem( "weapon_crowbar" );
GiveNamedItem( "weapon_9mmhandgun" );
GiveNamedItem( "ammo_9mmclip" );
GiveNamedItem( "weapon_shotgun" );
GiveNamedItem( "ammo_buckshot" );
GiveNamedItem( "weapon_9mmAR" );
GiveNamedItem( "ammo_9mmAR" );
GiveNamedItem( "weapon_rock" );
GiveNamedItem( "weapon_katana" );
GiveNamedItem( "weapon_pepsigun" );
9 years ago
GiveNamedItem( "ammo_ARgrenades" );
GiveNamedItem( "weapon_handgrenade" );
GiveNamedItem( "weapon_tripmine" );
#ifndef OEM_BUILD
GiveNamedItem( "weapon_357" );
GiveNamedItem( "ammo_357" );
GiveNamedItem( "weapon_crossbow" );
GiveNamedItem( "ammo_crossbow" );
GiveNamedItem( "weapon_egon" );
GiveNamedItem( "weapon_needle" );
9 years ago
GiveNamedItem( "weapon_gauss" );
GiveNamedItem( "weapon_sawnoff" );
9 years ago
GiveNamedItem( "weapon_rpg" );
GiveNamedItem( "weapon_snipars" );
GiveNamedItem( "ammo_gaussclip" );
GiveNamedItem( "ammo_snipars" );
9 years ago
GiveNamedItem( "ammo_rpgclip" );
GiveNamedItem( "weapon_satchel" );
GiveNamedItem( "weapon_snark" );
GiveNamedItem( "weapon_hornetgun" );
#endif
gEvilImpulse101 = FALSE;
break;
case 102:
// Gibbage!!!
CGib::SpawnRandomGibs( pev, 1, 1 );
break;
case 103:
// What the hell are you doing?
pEntity = FindEntityForward( this );
8 years ago
if( pEntity )
9 years ago
{
CBaseMonster *pMonster = pEntity->MyMonsterPointer();
8 years ago
if( pMonster )
9 years ago
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.
8 years ago
if( m_fNoPlayerSound )
9 years ago
{
8 years ago
ALERT( at_console, "Player is audible\n" );
m_fNoPlayerSound = FALSE;
}
else
{
ALERT( at_console, "Player is silent\n" );
m_fNoPlayerSound = TRUE;
9 years ago
}
8 years ago
break;
9 years ago
case 106:
// Give me the classname and targetname of this entity.
pEntity = FindEntityForward( this );
8 years ago
if( pEntity )
9 years ago
{
8 years ago
ALERT( at_console, "Classname: %s", STRING( pEntity->pev->classname ) );
if( !FStringNull( pEntity->pev->targetname ) )
9 years ago
{
8 years ago
ALERT( at_console, " - Targetname: %s\n", STRING( pEntity->pev->targetname ) );
9 years ago
}
else
{
8 years ago
ALERT( at_console, " - TargetName: No Targetname\n" );
9 years ago
}
8 years ago
ALERT( at_console, "Model: %s\n", STRING( pEntity->pev->model ) );
if( pEntity->pev->globalname )
ALERT( at_console, "Globalname: %s\n", STRING( pEntity->pev->globalname ) );
9 years ago
}
break;
case 107:
{
//TraceResult tr;
8 years ago
edict_t *pWorld = g_engfuncs.pfnPEntityOfEntIndex( 0 );
9 years ago
Vector start = pev->origin + pev->view_ofs;
Vector end = start + gpGlobals->v_forward * 1024;
UTIL_TraceLine( start, end, ignore_monsters, edict(), &tr );
8 years ago
if( tr.pHit )
9 years ago
pWorld = tr.pHit;
const char *pTextureName = TRACE_TEXTURE( pWorld, start, end );
8 years ago
if( pTextureName )
9 years ago
ALERT( at_console, "Texture: %s\n", pTextureName );
}
break;
8 years ago
case 195:
// show shortest paths for entire level to nearest node
9 years ago
{
8 years ago
Create( "node_viewer_fly", pev->origin, pev->angles );
9 years ago
}
break;
8 years ago
case 196:
// show shortest paths for entire level to nearest node
9 years ago
{
8 years ago
Create( "node_viewer_large", pev->origin, pev->angles );
9 years ago
}
break;
8 years ago
case 197:
// show shortest paths for entire level to nearest node
9 years ago
{
8 years ago
Create( "node_viewer_human", pev->origin, pev->angles );
9 years ago
}
break;
8 years ago
case 199:
// show nearest node and all connections
9 years ago
{
8 years ago
ALERT( at_console, "%d\n", WorldGraph.FindNearestNode( pev->origin, bits_NODE_GROUP_REALM ) );
WorldGraph.ShowNodeConnections( WorldGraph.FindNearestNode( pev->origin, bits_NODE_GROUP_REALM ) );
9 years ago
}
break;
8 years ago
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 );
9 years ago
8 years ago
if( tr.flFraction != 1.0 )
{
// line hit something, so paint a decal
8 years ago
CBloodSplat *pBlood = GetClassPtr( (CBloodSplat *)NULL );
9 years ago
pBlood->Spawn( pev );
}
break;
8 years ago
case 203:
// remove creature.
9 years ago
pEntity = FindEntityForward( this );
8 years ago
if( pEntity )
9 years ago
{
8 years ago
if( pEntity->pev->takedamage )
pEntity->SetThink( &CBaseEntity::SUB_Remove );
9 years ago
}
break;
}
#endif // HLDEMO_BUILD
}
//
// Add a weapon to the player (Item == Weapon == Selectable Object)
//
int CBasePlayer::AddPlayerItem( CBasePlayerItem *pItem )
{
CBasePlayerItem *pInsert;
9 years ago
pInsert = m_rgpPlayerItems[pItem->iItemSlot()];
8 years ago
while( pInsert )
9 years ago
{
8 years ago
if( FClassnameIs( pInsert->pev, STRING( pItem->pev->classname ) ) )
9 years ago
{
8 years ago
if( pItem->AddDuplicate( pInsert ) )
9 years ago
{
8 years ago
g_pGameRules->PlayerGotWeapon( this, pItem );
9 years ago
pItem->CheckRespawn();
// ugly hack to update clip w/o an update clip message
8 years ago
pInsert->UpdateItemInfo();
if( m_pActiveItem )
m_pActiveItem->UpdateItemInfo();
9 years ago
8 years ago
pItem->Kill();
9 years ago
}
8 years ago
else if( gEvilImpulse101 )
9 years ago
{
// FIXME: remove anyway for deathmatch testing
8 years ago
pItem->Kill();
9 years ago
}
return FALSE;
}
pInsert = pInsert->m_pNext;
}
8 years ago
if( pItem->AddToPlayer( this ) )
9 years ago
{
8 years ago
g_pGameRules->PlayerGotWeapon( this, pItem );
9 years ago
pItem->CheckRespawn();
pItem->m_pNext = m_rgpPlayerItems[pItem->iItemSlot()];
m_rgpPlayerItems[pItem->iItemSlot()] = pItem;
// should we switch to this item?
8 years ago
if( g_pGameRules->FShouldSwitchWeapon( this, pItem ) )
9 years ago
{
SwitchWeapon( pItem );
}
return TRUE;
}
8 years ago
else if( gEvilImpulse101 )
9 years ago
{
// FIXME: remove anyway for deathmatch testing
8 years ago
pItem->Kill();
9 years ago
}
return FALSE;
}
int CBasePlayer::RemovePlayerItem( CBasePlayerItem *pItem )
{
8 years ago
if( m_pActiveItem == pItem )
9 years ago
{
8 years ago
ResetAutoaim();
pItem->Holster();
9 years ago
pItem->pev->nextthink = 0;// crowbar may be trying to swing again, etc.
pItem->SetThink( NULL );
m_pActiveItem = NULL;
pev->viewmodel = 0;
pev->weaponmodel = 0;
}
8 years ago
else if( m_pLastItem == pItem )
9 years ago
m_pLastItem = NULL;
CBasePlayerItem *pPrev = m_rgpPlayerItems[pItem->iItemSlot()];
8 years ago
if( pPrev == pItem )
9 years ago
{
m_rgpPlayerItems[pItem->iItemSlot()] = pItem->m_pNext;
return TRUE;
}
else
{
8 years ago
while( pPrev && pPrev->m_pNext != pItem )
9 years ago
{
pPrev = pPrev->m_pNext;
}
8 years ago
if( pPrev )
9 years ago
{
pPrev->m_pNext = pItem->m_pNext;
return TRUE;
}
}
return FALSE;
}
//
// Returns the unique ID for the ammo, or -1 if error
//
8 years ago
int CBasePlayer::GiveAmmo( int iCount, char *szName, int iMax )
9 years ago
{
8 years ago
if( !szName )
9 years ago
{
// no ammo.
return -1;
}
8 years ago
if( !g_pGameRules->CanHaveAmmo( this, szName, iMax ) )
9 years ago
{
// game rules say I can't have any more of this ammo type.
return -1;
}
int i = 0;
i = GetAmmoIndex( szName );
8 years ago
if( i < 0 || i >= MAX_AMMO_SLOTS )
9 years ago
return -1;
int iAdd = min( iCount, iMax - m_rgAmmo[i] );
8 years ago
if( iAdd < 1 )
9 years ago
return i;
8 years ago
m_rgAmmo[i] += iAdd;
9 years ago
8 years ago
if( gmsgAmmoPickup ) // make sure the ammo messages have been linked first
9 years ago
{
// Send the message that ammo has been picked up
MESSAGE_BEGIN( MSG_ONE, gmsgAmmoPickup, NULL, pev );
8 years ago
WRITE_BYTE( GetAmmoIndex( szName ) ); // ammo ID
9 years ago
WRITE_BYTE( iAdd ); // amount
MESSAGE_END();
}
TabulateAmmo();
return i;
}
/*
============
ItemPreFrame
Called every frame by the player PreThink
============
*/
void CBasePlayer::ItemPreFrame()
{
#if defined( CLIENT_WEAPONS )
8 years ago
if( m_flNextAttack > 0 )
9 years ago
#else
8 years ago
if( gpGlobals->time < m_flNextAttack )
9 years ago
#endif
{
return;
}
8 years ago
if( !m_pActiveItem )
9 years ago
return;
8 years ago
m_pActiveItem->ItemPreFrame();
9 years ago
}
/*
============
ItemPostFrame
Called every frame by the player PostThink
============
*/
void CBasePlayer::ItemPostFrame()
{
static int fInSelect = FALSE;
// check if the player is using a tank
8 years ago
if( m_pTank != NULL )
9 years ago
return;
#if defined( CLIENT_WEAPONS )
8 years ago
if( m_flNextAttack > 0 )
9 years ago
#else
8 years ago
if( gpGlobals->time < m_flNextAttack )
9 years ago
#endif
{
return;
}
ImpulseCommands();
8 years ago
if( !m_pActiveItem )
9 years ago
return;
8 years ago
m_pActiveItem->ItemPostFrame();
9 years ago
}
int CBasePlayer::AmmoInventory( int iAmmoIndex )
{
8 years ago
if( iAmmoIndex == -1 )
9 years ago
{
return -1;
}
8 years ago
return m_rgAmmo[iAmmoIndex];
9 years ago
}
8 years ago
int CBasePlayer::GetAmmoIndex( const char *psz )
9 years ago
{
int i;
8 years ago
if( !psz )
9 years ago
return -1;
8 years ago
for( i = 1; i < MAX_AMMO_SLOTS; i++ )
9 years ago
{
8 years ago
if( !CBasePlayerItem::AmmoInfoArray[i].pszName )
9 years ago
continue;
8 years ago
if( stricmp( psz, CBasePlayerItem::AmmoInfoArray[i].pszName ) == 0 )
9 years ago
return i;
}
return -1;
}
// Called from UpdateClientData
// makes sure the client has all the necessary ammo info, if values have changed
8 years ago
void CBasePlayer::SendAmmoUpdate( void )
9 years ago
{
8 years ago
for( int i = 0; i < MAX_AMMO_SLOTS; i++ )
9 years ago
{
8 years ago
if( m_rgAmmo[i] != m_rgAmmoLast[i] )
9 years ago
{
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( max( 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.
=========================================================
*/
8 years ago
void CBasePlayer::UpdateClientData( void )
9 years ago
{
if ( m_iFOV != 0 && m_iFOV != 20 && m_iFOV != 40 )
{
MESSAGE_BEGIN( MSG_ONE, gmsgZoom, NULL, pev );
WRITE_BYTE(1);
MESSAGE_END();
}
8 years ago
if( m_fInitHUD )
9 years ago
{
m_fInitHUD = FALSE;
gInitHUD = FALSE;
MESSAGE_BEGIN( MSG_ONE, gmsgResetHUD, NULL, pev );
WRITE_BYTE( 0 );
MESSAGE_END();
8 years ago
if( !m_fGameHUDInitialized )
9 years ago
{
MESSAGE_BEGIN( MSG_ONE, gmsgInitHUD, NULL, pev );
MESSAGE_END();
g_pGameRules->InitHUD( this );
m_fGameHUDInitialized = TRUE;
8 years ago
if( g_pGameRules->IsMultiplayer() )
9 years ago
{
FireTargets( "game_playerjoin", this, this, USE_TOGGLE, 0 );
}
}
FireTargets( "game_playerspawn", this, this, USE_TOGGLE, 0 );
InitStatusBar();
}
8 years ago
if( m_iHideHUD != m_iClientHideHUD )
9 years ago
{
MESSAGE_BEGIN( MSG_ONE, gmsgHideWeapon, NULL, pev );
WRITE_BYTE( m_iHideHUD );
MESSAGE_END();
m_iClientHideHUD = m_iHideHUD;
}
8 years ago
if( m_iFOV != m_iClientFOV )
9 years ago
{
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
8 years ago
if( gDisplayTitle )
9 years ago
{
MESSAGE_BEGIN( MSG_ONE, gmsgShowGameTitle, NULL, pev );
WRITE_BYTE( 0 );
MESSAGE_END();
gDisplayTitle = 0;
}
8 years ago
if( pev->health != m_iClientHealth )
9 years ago
{
int iHealth = max( pev->health, 0 ); // make sure that no negative health values are sent
// send "health" update message
MESSAGE_BEGIN( MSG_ONE, gmsgHealth, NULL, pev );
WRITE_BYTE( iHealth );
MESSAGE_END();
m_iClientHealth = pev->health;
}
8 years ago
if( pev->armorvalue != m_iClientBattery )
9 years ago
{
m_iClientBattery = pev->armorvalue;
ASSERT( gmsgBattery > 0 );
9 years ago
// send "health" update message
MESSAGE_BEGIN( MSG_ONE, gmsgBattery, NULL, pev );
8 years ago
WRITE_SHORT( (int)pev->armorvalue );
9 years ago
MESSAGE_END();
}
8 years ago
if( pev->dmg_take || pev->dmg_save || m_bitsHUDDamage != m_bitsDamageType )
9 years ago
{
// 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;
8 years ago
if( other )
9 years ago
{
8 years ago
CBaseEntity *pEntity = CBaseEntity::Instance( other );
if( pEntity )
9 years ago
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( pev->dmg_save );
WRITE_BYTE( pev->dmg_take );
WRITE_LONG( visibleDamageBits );
WRITE_COORD( damageOrigin.x );
WRITE_COORD( damageOrigin.y );
WRITE_COORD( damageOrigin.z );
MESSAGE_END();
8 years ago
9 years ago
pev->dmg_take = 0;
pev->dmg_save = 0;
m_bitsHUDDamage = m_bitsDamageType;
8 years ago
9 years ago
// Clear off non-time-based damage indicators
m_bitsDamageType &= DMG_TIMEBASED;
}
// Update Flashlight
8 years ago
if( ( m_flFlashLightTime ) && ( m_flFlashLightTime <= gpGlobals->time ) )
9 years ago
{
8 years ago
if( FlashlightIsOn() )
9 years ago
{
8 years ago
if( m_iFlashBattery )
9 years ago
{
m_flFlashLightTime = FLASH_DRAIN_TIME + gpGlobals->time;
m_iFlashBattery--;
8 years ago
if( !m_iFlashBattery )
9 years ago
FlashlightTurnOff();
}
}
else
{
8 years ago
if( m_iFlashBattery < 100 )
9 years ago
{
m_flFlashLightTime = FLASH_CHARGE_TIME + gpGlobals->time;
m_iFlashBattery++;
}
else
m_flFlashLightTime = 0;
}
MESSAGE_BEGIN( MSG_ONE, gmsgFlashBattery, NULL, pev );
8 years ago
WRITE_BYTE( m_iFlashBattery );
9 years ago
MESSAGE_END();
}
8 years ago
if( m_iTrain & TRAIN_NEW )
9 years ago
{
ASSERT( gmsgTrain > 0 );
9 years ago
// send "health" update message
MESSAGE_BEGIN( MSG_ONE, gmsgTrain, NULL, pev );
8 years ago
WRITE_BYTE( m_iTrain & 0xF );
9 years ago
MESSAGE_END();
m_iTrain &= ~TRAIN_NEW;
}
//
// New Weapon?
//
8 years ago
if( !m_fKnownItem )
9 years ago
{
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
8 years ago
// byte flags
9 years ago
// ???? Icons
9 years ago
// Send ALL the weapon info now
int i;
8 years ago
for( i = 0; i < MAX_WEAPONS; i++ )
9 years ago
{
ItemInfo& II = CBasePlayerItem::ItemInfoArray[i];
8 years ago
if( !II.iId )
9 years ago
continue;
const char *pszName;
8 years ago
if( !II.pszName )
9 years ago
pszName = "Empty";
else
pszName = II.pszName;
MESSAGE_BEGIN( MSG_ONE, gmsgWeaponList, NULL, pev );
8 years ago
WRITE_STRING( pszName ); // string weapon name
WRITE_BYTE( GetAmmoIndex( II.pszAmmo1 ) ); // byte Ammo Type
WRITE_BYTE( II.iMaxAmmo1 ); // byte Max Ammo 1
WRITE_BYTE( GetAmmoIndex( II.pszAmmo2 ) ); // byte Ammo2 Type
WRITE_BYTE( II.iMaxAmmo2 ); // byte Max Ammo 2
WRITE_BYTE( II.iSlot ); // byte bucket
WRITE_BYTE( II.iPosition ); // byte bucket pos
WRITE_BYTE( II.iId ); // byte id (bit index into pev->weapons)
WRITE_BYTE( II.iFlags ); // byte Flags
9 years ago
MESSAGE_END();
}
}
SendAmmoUpdate();
// Update all the items
8 years ago
for( int i = 0; i < MAX_ITEM_TYPES; i++ )
9 years ago
{
8 years ago
if( m_rgpPlayerItems[i] ) // each item updates it's successors
9 years ago
m_rgpPlayerItems[i]->UpdateClientData( this );
}
// Cache and client weapon change
m_pClientActiveItem = m_pActiveItem;
m_iClientFOV = m_iFOV;
// Update Status Bar
8 years ago
if( m_flNextSBarUpdateTime < gpGlobals->time )
9 years ago
{
UpdateStatusBar();
m_flNextSBarUpdateTime = gpGlobals->time + 0.2;
}
}
//=========================================================
// FBecomeProne - Overridden for the player to set the proper
// physics flags when a barnacle grabs player.
//=========================================================
8 years ago
BOOL CBasePlayer::FBecomeProne( void )
9 years ago
{
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.
//=========================================================
8 years ago
void CBasePlayer::BarnacleVictimBitten( entvars_t *pevBarnacle )
9 years ago
{
8 years ago
TakeDamage( pevBarnacle, pevBarnacle, pev->health + pev->armorvalue, DMG_SLASH | DMG_ALWAYSGIB );
9 years ago
}
//=========================================================
// BarnacleVictimReleased - overridden for player who has
// physics flags concerns.
//=========================================================
8 years ago
void CBasePlayer::BarnacleVictimReleased( void )
9 years ago
{
m_afPhysicsFlags &= ~PFLAG_ONBARNACLE;
}
//=========================================================
// Illumination
// return player light level plus virtual muzzle flash
//=========================================================
8 years ago
int CBasePlayer::Illumination( void )
9 years ago
{
8 years ago
int iIllum = CBaseEntity::Illumination();
9 years ago
iIllum += m_iWeaponFlash;
8 years ago
if( iIllum > 255 )
9 years ago
return 255;
return iIllum;
}
8 years ago
void CBasePlayer::EnableControl( BOOL fControl )
9 years ago
{
8 years ago
if( !fControl )
9 years ago
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
//=========================================================
8 years ago
Vector CBasePlayer::GetAutoaimVector( float flDelta )
9 years ago
{
8 years ago
if( g_iSkillLevel == SKILL_HARD )
9 years ago
{
UTIL_MakeVectors( pev->v_angle + pev->punchangle );
return gpGlobals->v_forward;
}
8 years ago
Vector vecSrc = GetGunPosition();
9 years ago
float flDist = 8192;
// always use non-sticky autoaim
// UNDONE: use sever variable to chose!
8 years ago
if( 1 || g_iSkillLevel == SKILL_MEDIUM )
9 years ago
{
m_vecAutoAim = Vector( 0, 0, 0 );
// flDelta *= 0.5;
}
BOOL m_fOldTargeting = m_fOnTarget;
Vector angles = AutoaimDeflection(vecSrc, flDist, flDelta );
// update ontarget if changed
8 years ago
if( !g_pGameRules->AllowAutoTargetCrosshair() )
9 years ago
m_fOnTarget = 0;
8 years ago
else if( m_fOldTargeting != m_fOnTarget )
9 years ago
{
8 years ago
m_pActiveItem->UpdateItemInfo();
9 years ago
}
8 years ago
if( angles.x > 180 )
9 years ago
angles.x -= 360;
8 years ago
if( angles.x < -180 )
9 years ago
angles.x += 360;
8 years ago
if( angles.y > 180 )
9 years ago
angles.y -= 360;
8 years ago
if( angles.y < -180 )
9 years ago
angles.y += 360;
8 years ago
if( angles.x > 25 )
9 years ago
angles.x = 25;
8 years ago
if( angles.x < -25 )
9 years ago
angles.x = -25;
8 years ago
if( angles.y > 12 )
9 years ago
angles.y = 12;
8 years ago
if( angles.y < -12 )
9 years ago
angles.y = -12;
// always use non-sticky autoaim
// UNDONE: use sever variable to chose!
8 years ago
if( 0 || g_iSkillLevel == SKILL_EASY )
9 years ago
{
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
8 years ago
if( g_psv_aim->value != 0 )
9 years ago
{
8 years ago
if( m_vecAutoAim.x != m_lastx || m_vecAutoAim.y != m_lasty )
9 years ago
{
SET_CROSSHAIRANGLE( edict(), -m_vecAutoAim.x, m_vecAutoAim.y );
8 years ago
9 years ago
m_lastx = m_vecAutoAim.x;
m_lasty = m_vecAutoAim.y;
}
}
// ALERT( at_console, "%f %f\n", angles.x, angles.y );
UTIL_MakeVectors( pev->v_angle + pev->punchangle + m_vecAutoAim );
return gpGlobals->v_forward;
}
8 years ago
Vector CBasePlayer::AutoaimDeflection( Vector &vecSrc, float flDist, float flDelta )
9 years ago
{
8 years ago
edict_t *pEdict = g_engfuncs.pfnPEntityOfEntIndex( 1 );
CBaseEntity *pEntity;
float bestdot;
Vector bestdir;
edict_t *bestent;
9 years ago
TraceResult tr;
8 years ago
if( g_psv_aim->value == 0 )
9 years ago
{
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 );
8 years ago
if( tr.pHit && tr.pHit->v.takedamage != DAMAGE_NO )
9 years ago
{
// don't look through water
8 years ago
if( !( ( pev->waterlevel != 3 && tr.pHit->v.waterlevel == 3 ) || ( pev->waterlevel == 3 && tr.pHit->v.waterlevel == 0 ) ) )
9 years ago
{
8 years ago
if( tr.pHit->v.takedamage == DAMAGE_AIM )
9 years ago
m_fOnTarget = TRUE;
return m_vecAutoAim;
}
}
8 years ago
for( int i = 1; i < gpGlobals->maxEntities; i++, pEdict++ )
9 years ago
{
Vector center;
Vector dir;
float dot;
8 years ago
if( pEdict->free ) // Not in use
9 years ago
continue;
8 years ago
if( pEdict->v.takedamage != DAMAGE_AIM )
9 years ago
continue;
8 years ago
if( pEdict == edict() )
9 years ago
continue;
8 years ago
//if( pev->team > 0 && pEdict->v.team == pev->team )
// continue; // don't aim at teammate
8 years ago
if( !g_pGameRules->ShouldAutoAim( this, pEdict ) )
9 years ago
continue;
pEntity = Instance( pEdict );
8 years ago
if( pEntity == NULL )
9 years ago
continue;
8 years ago
if( !pEntity->IsAlive() )
9 years ago
continue;
// don't look through water
8 years ago
if( ( pev->waterlevel != 3 && pEntity->pev->waterlevel == 3 ) || ( pev->waterlevel == 3 && pEntity->pev->waterlevel == 0 ) )
9 years ago
continue;
center = pEntity->BodyTarget( vecSrc );
8 years ago
dir = ( center - vecSrc ).Normalize();
9 years ago
// make sure it's in front of the player
8 years ago
if( DotProduct( dir, gpGlobals->v_forward ) < 0 )
9 years ago
continue;
8 years ago
dot = fabs( DotProduct( dir, gpGlobals->v_right ) ) + fabs( DotProduct( dir, gpGlobals->v_up ) ) * 0.5;
9 years ago
// tweek for distance
8 years ago
dot *= 1.0 + 0.2 * ( ( center - vecSrc ).Length() / flDist );
9 years ago
8 years ago
if( dot > bestdot )
9 years ago
continue; // to far to turn
UTIL_TraceLine( vecSrc, center, dont_ignore_monsters, edict(), &tr );
8 years ago
if( tr.flFraction != 1.0 && tr.pHit != pEdict )
9 years ago
{
// 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
8 years ago
if( IRelationship( pEntity ) < 0 )
9 years ago
{
8 years ago
if( !pEntity->IsPlayer() && !g_pGameRules->IsDeathmatch() )
// ALERT( at_console, "friend\n" );
9 years ago
continue;
}
// can shoot at this one
bestdot = dot;
bestent = pEdict;
bestdir = dir;
}
8 years ago
if( bestent )
9 years ago
{
8 years ago
bestdir = UTIL_VecToAngles( bestdir );
9 years ago
bestdir.x = -bestdir.x;
bestdir = bestdir - pev->v_angle - pev->punchangle;
8 years ago
if( bestent->v.takedamage == DAMAGE_AIM )
9 years ago
m_fOnTarget = TRUE;
return bestdir;
}
return Vector( 0, 0, 0 );
}
8 years ago
void CBasePlayer::ResetAutoaim()
9 years ago
{
8 years ago
if( m_vecAutoAim.x != 0 || m_vecAutoAim.y != 0 )
9 years ago
{
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.
=============
*/
8 years ago
void CBasePlayer::SetCustomDecalFrames( int nFrames )
9 years ago
{
8 years ago
if( nFrames > 0 && nFrames < 8 )
9 years ago
m_nCustomSprayFrames = nFrames;
else
m_nCustomSprayFrames = -1;
}
/*
=============
GetCustomDecalFrames
Returns the # of custom frames this player's custom clan logo contains.
=============
*/
8 years ago
int CBasePlayer::GetCustomDecalFrames( void )
9 years ago
{
return m_nCustomSprayFrames;
}
//=========================================================
// DropPlayerItem - drop the named item, or if no name,
// the active item.
//=========================================================
8 years ago
void CBasePlayer::DropPlayerItem( char *pszItemName )
9 years ago
{
8 years ago
if( !g_pGameRules->IsMultiplayer() || ( weaponstay.value > 0 ) )
9 years ago
{
// no dropping in single player.
return;
}
8 years ago
if( !strlen( pszItemName ) )
9 years ago
{
// 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;
8 years ago
for( i = 0; i < MAX_ITEM_TYPES; i++ )
9 years ago
{
8 years ago
pWeapon = m_rgpPlayerItems[i];
9 years ago
8 years ago
while( pWeapon )
9 years ago
{
8 years ago
if( pszItemName )
9 years ago
{
// try to match by name.
8 years ago
if( !strcmp( pszItemName, STRING( pWeapon->pev->classname ) ) )
9 years ago
{
// match!
break;
}
}
else
{
// trying to drop active item
8 years ago
if( pWeapon == m_pActiveItem )
9 years ago
{
// 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.
8 years ago
if( pWeapon )
9 years ago
{
g_pGameRules->GetNextBestWeapon( this, pWeapon );
8 years ago
UTIL_MakeVectors( pev->angles );
9 years ago
8 years ago
pev->weapons &= ~( 1 << pWeapon->m_iId );// take item off hud
9 years ago
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.
8 years ago
int iAmmoIndex;
9 years ago
8 years ago
iAmmoIndex = GetAmmoIndex( pWeapon->pszAmmo1() ); // ???
if( iAmmoIndex != -1 )
9 years ago
{
// this weapon weapon uses ammo, so pack an appropriate amount.
8 years ago
if( pWeapon->iFlags() & ITEM_FLAG_EXHAUSTIBLE )
9 years ago
{
// pack up all the ammo, this weapon is its own ammo type
8 years ago
pWeaponBox->PackAmmo( MAKE_STRING( pWeapon->pszAmmo1() ), m_rgAmmo[iAmmoIndex] );
m_rgAmmo[iAmmoIndex] = 0;
9 years ago
}
else
{
// pack half of the ammo
8 years ago
pWeaponBox->PackAmmo( MAKE_STRING( pWeapon->pszAmmo1() ), m_rgAmmo[iAmmoIndex] / 2 );
m_rgAmmo[iAmmoIndex] /= 2;
9 years ago
}
}
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()];
8 years ago
while( pItem )
9 years ago
{
8 years ago
if( FClassnameIs( pItem->pev, STRING( pCheckItem->pev->classname ) ) )
9 years ago
{
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;
8 years ago
for( i = 0; i < MAX_ITEM_TYPES; i++ )
9 years ago
{
8 years ago
pItem = m_rgpPlayerItems[i];
while( pItem )
9 years ago
{
8 years ago
if( !strcmp( pszItemName, STRING( pItem->pev->classname ) ) )
9 years ago
{
return TRUE;
}
pItem = pItem->m_pNext;
}
}
return FALSE;
}
//=========================================================
//
//=========================================================
8 years ago
BOOL CBasePlayer::SwitchWeapon( CBasePlayerItem *pWeapon )
9 years ago
{
8 years ago
if( !pWeapon->CanDeploy() )
9 years ago
{
return FALSE;
}
8 years ago
ResetAutoaim();
if( m_pActiveItem )
9 years ago
{
8 years ago
m_pActiveItem->Holster();
9 years ago
}
m_pActiveItem = pWeapon;
8 years ago
pWeapon->Deploy();
9 years ago
return TRUE;
}
//=========================================================
// Dead HEV suit prop
//=========================================================
class CDeadHEV : public CBaseMonster
{
public:
void Spawn( void );
8 years ago
int Classify( void )
{
return CLASS_HUMAN_MILITARY;
}
9 years ago
void KeyValue( KeyValueData *pkvd );
8 years ago
int m_iPose;// which sequence to display -- temporary, don't need to save
9 years ago
static char *m_szPoses[4];
};
8 years ago
char *CDeadHEV::m_szPoses[] =
{
"deadback",
"deadsitting",
"deadstomach",
"deadtable"
};
9 years ago
void CDeadHEV::KeyValue( KeyValueData *pkvd )
{
8 years ago
if( FStrEq( pkvd->szKeyName, "pose" ) )
9 years ago
{
8 years ago
m_iPose = atoi( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else
9 years ago
CBaseMonster::KeyValue( pkvd );
}
LINK_ENTITY_TO_CLASS( monster_hevsuit_dead, CDeadHEV )
9 years ago
//=========================================================
// ********** DeadHEV SPAWN **********
//=========================================================
8 years ago
void CDeadHEV::Spawn( void )
9 years ago
{
8 years ago
PRECACHE_MODEL( "models/player.mdl" );
SET_MODEL( ENT( pev ), "models/player.mdl" );
9 years ago
8 years ago
pev->effects = 0;
pev->yaw_speed = 8;
pev->sequence = 0;
pev->body = 1;
m_bloodColor = BLOOD_COLOR_RED;
9 years ago
pev->sequence = LookupSequence( m_szPoses[m_iPose] );
8 years ago
if( pev->sequence == -1 )
9 years ago
{
8 years ago
ALERT( at_console, "Dead hevsuit with bad pose\n" );
9 years ago
pev->sequence = 0;
pev->effects = EF_BRIGHTFIELD;
}
// Corpses have less health
8 years ago
pev->health = 8;
9 years ago
MonsterInitDead();
}
class CStripWeapons : public CPointEntity
{
public:
8 years ago
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
9 years ago
private:
};
LINK_ENTITY_TO_CLASS( player_weaponstrip, CStripWeapons )
9 years ago
8 years ago
void CStripWeapons::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
9 years ago
{
CBasePlayer *pPlayer = NULL;
8 years ago
if( pActivator && pActivator->IsPlayer() )
9 years ago
{
pPlayer = (CBasePlayer *)pActivator;
}
8 years ago
else if( !g_pGameRules->IsDeathmatch() )
9 years ago
{
pPlayer = (CBasePlayer *)CBaseEntity::Instance( g_engfuncs.pfnPEntityOfEntIndex( 1 ) );
}
8 years ago
if( pPlayer )
9 years ago
pPlayer->RemoveAllItems( FALSE );
}
class CRevertSaved : public CPointEntity
{
public:
8 years ago
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT MessageThink( void );
void EXPORT LoadThink( void );
void KeyValue( KeyValueData *pkvd );
9 years ago
8 years ago
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
9 years ago
8 years ago
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; }
9 years ago
8 years ago
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; }
9 years ago
private:
8 years ago
float m_messageTime;
float m_loadTime;
9 years ago
};
LINK_ENTITY_TO_CLASS( player_loadsaved, CRevertSaved )
9 years ago
TYPEDESCRIPTION CRevertSaved::m_SaveData[] =
9 years ago
{
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 )
9 years ago
8 years ago
void CRevertSaved::KeyValue( KeyValueData *pkvd )
9 years ago
{
8 years ago
if( FStrEq( pkvd->szKeyName, "duration" ) )
9 years ago
{
8 years ago
SetDuration( atof( pkvd->szValue ) );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "holdtime" ) )
9 years ago
{
8 years ago
SetHoldTime( atof( pkvd->szValue ) );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "messagetime" ) )
9 years ago
{
8 years ago
SetMessageTime( atof( pkvd->szValue ) );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "loadtime" ) )
9 years ago
{
8 years ago
SetLoadTime( atof( pkvd->szValue ) );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else
9 years ago
CPointEntity::KeyValue( pkvd );
}
8 years ago
void CRevertSaved::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
9 years ago
{
UTIL_ScreenFadeAll( pev->rendercolor, Duration(), HoldTime(), pev->renderamt, FFADE_OUT );
pev->nextthink = gpGlobals->time + MessageTime();
SetThink( &CRevertSaved::MessageThink );
}
8 years ago
void CRevertSaved::MessageThink( void )
9 years ago
{
8 years ago
UTIL_ShowMessageAll( STRING( pev->message ) );
9 years ago
float nextThink = LoadTime() - MessageTime();
8 years ago
if( nextThink > 0 )
9 years ago
{
pev->nextthink = gpGlobals->time + nextThink;
SetThink( &CRevertSaved::LoadThink );
}
else
LoadThink();
}
8 years ago
void CRevertSaved::LoadThink( void )
9 years ago
{
8 years ago
if( !gpGlobals->deathmatch )
9 years ago
{
8 years ago
SERVER_COMMAND( "reload\n" );
9 years ago
}
}
//=========================================================
// 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!
}
8 years ago
void CInfoIntermission::Think( void )
9 years ago
{
edict_t *pTarget;
// find my target
8 years ago
pTarget = FIND_ENTITY_BY_TARGETNAME( NULL, STRING( pev->target ) );
9 years ago
8 years ago
if( !FNullEnt( pTarget ) )
9 years ago
{
8 years ago
pev->v_angle = UTIL_VecToAngles( ( pTarget->v.origin - pev->origin ).Normalize() );
9 years ago
pev->v_angle.x = -pev->v_angle.x;
}
}
LINK_ENTITY_TO_CLASS( info_intermission, CInfoIntermission )