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.
8267 lines
217 KiB
8267 lines
217 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Player for HL1. |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "cs_player.h" |
|
#include "cs_gamerules.h" |
|
#include "trains.h" |
|
#include "vcollide_parse.h" |
|
#include "in_buttons.h" |
|
#include "igamemovement.h" |
|
#include "ai_hull.h" |
|
#include "ndebugoverlay.h" |
|
#include "weapon_csbase.h" |
|
#include "decals.h" |
|
#include "cs_ammodef.h" |
|
#include "IEffects.h" |
|
#include "cs_client.h" |
|
#include "client.h" |
|
#include "cs_shareddefs.h" |
|
#include "shake.h" |
|
#include "team.h" |
|
#include "weapon_c4.h" |
|
#include "weapon_parse.h" |
|
#include "weapon_knife.h" |
|
#include "movehelper_server.h" |
|
#include "tier0/vprof.h" |
|
#include "te_effect_dispatch.h" |
|
#include "vphysics/player_controller.h" |
|
#include "weapon_hegrenade.h" |
|
#include "weapon_flashbang.h" |
|
#include "weapon_csbasegun.h" |
|
#include "weapon_smokegrenade.h" |
|
#include <KeyValues.h> |
|
#include "engine/IEngineSound.h" |
|
#include "bot.h" |
|
#include "studio.h" |
|
#include <coordsize.h> |
|
#include "predicted_viewmodel.h" |
|
#include "props_shared.h" |
|
#include "tier0/icommandline.h" |
|
#include "info_camera_link.h" |
|
#include "hintmessage.h" |
|
#include "obstacle_pushaway.h" |
|
#include "movevars_shared.h" |
|
#include "death_pose.h" |
|
#include "basecsgrenade_projectile.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
#include "CRagdollMagnet.h" |
|
#include "datacache/imdlcache.h" |
|
#include "npcevent.h" |
|
#include "cs_gamestats.h" |
|
#include "gamestats.h" |
|
#include "holiday_gift.h" |
|
#include "../../shared/cstrike/cs_achievement_constants.h" |
|
|
|
//============================================================================= |
|
// HPE_BEGIN |
|
//============================================================================= |
|
|
|
// [dwenger] Needed for global hostage list |
|
#include "cs_simple_hostage.h" |
|
|
|
// [dwenger] Needed for weapon type used tracking |
|
#include "../../shared/cstrike/cs_weapon_parse.h" |
|
|
|
#define REPORT_PLAYER_DAMAGE 0 |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#pragma optimize( "", off ) |
|
|
|
#pragma warning( disable : 4355 ) |
|
|
|
// Minimum interval between rate-limited commands that players can run. |
|
#define CS_COMMAND_MAX_RATE 0.3 |
|
|
|
const float CycleLatchInterval = 0.2f; |
|
|
|
#define CS_PUSHAWAY_THINK_CONTEXT "CSPushawayThink" |
|
|
|
ConVar cs_ShowStateTransitions( "cs_ShowStateTransitions", "-2", FCVAR_CHEAT, "cs_ShowStateTransitions <ent index or -1 for all>. Show player state transitions." ); |
|
ConVar sv_max_usercmd_future_ticks( "sv_max_usercmd_future_ticks", "8", 0, "Prevents clients from running usercmds too far in the future. Prevents speed hacks." ); |
|
ConVar sv_motd_unload_on_dismissal( "sv_motd_unload_on_dismissal", "0", 0, "If enabled, the MOTD contents will be unloaded when the player closes the MOTD." ); |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [Forrest] Allow MVP to be turned off for a server |
|
// [Forrest] Allow freezecam to be turned off for a server |
|
// [Forrest] Allow win panel to be turned off for a server |
|
//============================================================================= |
|
static void SvNoMVPChangeCallback( IConVar *pConVar, const char *pOldValue, float flOldValue ) |
|
{ |
|
ConVarRef var( pConVar ); |
|
if ( var.IsValid() && var.GetBool() ) |
|
{ |
|
// Clear the MVPs of all players when MVP is turned off. |
|
for ( int i = 1; i <= MAX_PLAYERS; i++ ) |
|
{ |
|
CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); |
|
|
|
if ( pPlayer ) |
|
{ |
|
pPlayer->SetNumMVPs( 0 ); |
|
} |
|
} |
|
} |
|
} |
|
ConVar sv_nomvp( "sv_nomvp", "0", 0, "Disable MVP awards.", SvNoMVPChangeCallback ); |
|
ConVar sv_disablefreezecam( "sv_disablefreezecam", "0", FCVAR_REPLICATED, "Turn on/off freezecam on server" ); |
|
ConVar sv_nowinpanel( "sv_nowinpanel", "0", FCVAR_REPLICATED, "Turn on/off win panel on server" ); |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
|
|
// ConVar bot_mimic( "bot_mimic", "0", FCVAR_CHEAT ); |
|
ConVar bot_freeze( "bot_freeze", "0", FCVAR_CHEAT ); |
|
ConVar bot_crouch( "bot_crouch", "0", FCVAR_CHEAT ); |
|
ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "180", FCVAR_CHEAT ); |
|
|
|
ConVar sv_legacy_grenade_damage( "sv_legacy_grenade_damage", "0", FCVAR_REPLICATED, "Enable to replicate grenade damage behavior of the original Counter-Strike Source game." ); |
|
|
|
extern ConVar mp_autokick; |
|
extern ConVar mp_holiday_nogifts; |
|
extern ConVar sv_turbophysics; |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Added in convars for freeze cam time length |
|
//============================================================================= |
|
|
|
extern ConVar spec_freeze_time; |
|
extern ConVar spec_freeze_traveltime; |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
extern ConVar ammo_hegrenade_max; |
|
extern ConVar ammo_flashbang_max; |
|
extern ConVar ammo_smokegrenade_max; |
|
|
|
|
|
#define THROWGRENADE_COUNTER_BITS 3 |
|
|
|
|
|
EHANDLE g_pLastCTSpawn; |
|
EHANDLE g_pLastTerroristSpawn; |
|
|
|
void TE_RadioIcon( IRecipientFilter& filter, float delay, CBaseEntity *pPlayer ); |
|
|
|
|
|
// -------------------------------------------------------------------------------- // |
|
// Classes |
|
// -------------------------------------------------------------------------------- // |
|
|
|
class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent |
|
{ |
|
public: |
|
int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position ) |
|
{ |
|
CCSPlayer *pPlayer = (CCSPlayer *)pObject->GetGameData(); |
|
if ( pPlayer ) |
|
{ |
|
if ( pPlayer->TouchedPhysics() ) |
|
{ |
|
return 0; |
|
} |
|
} |
|
return 1; |
|
} |
|
}; |
|
|
|
static CPhysicsPlayerCallback playerCallback; |
|
|
|
|
|
// -------------------------------------------------------------------------------- // |
|
// Ragdoll entities. |
|
// -------------------------------------------------------------------------------- // |
|
|
|
class CCSRagdoll : public CBaseAnimatingOverlay |
|
{ |
|
public: |
|
DECLARE_CLASS( CCSRagdoll, CBaseAnimatingOverlay ); |
|
DECLARE_SERVERCLASS(); |
|
|
|
// Transmit ragdolls to everyone. |
|
virtual int UpdateTransmitState() |
|
{ |
|
return SetTransmitState( FL_EDICT_ALWAYS ); |
|
} |
|
|
|
void Init( void ) |
|
{ |
|
SetSolid( SOLID_BBOX ); |
|
SetMoveType( MOVETYPE_STEP ); |
|
SetFriction( 1.0f ); |
|
SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); |
|
m_takedamage = DAMAGE_NO; |
|
SetCollisionGroup( COLLISION_GROUP_DEBRIS ); |
|
SetAbsOrigin( m_hPlayer->GetAbsOrigin() ); |
|
SetAbsVelocity( m_hPlayer->GetAbsVelocity() ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
ChangeTeam( m_hPlayer->GetTeamNumber() ); |
|
UseClientSideAnimation(); |
|
} |
|
|
|
public: |
|
// In case the client has the player entity, we transmit the player index. |
|
// In case the client doesn't have it, we transmit the player's model index, origin, and angles |
|
// so they can create a ragdoll in the right place. |
|
CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle |
|
CNetworkVector( m_vecRagdollVelocity ); |
|
CNetworkVector( m_vecRagdollOrigin ); |
|
CNetworkVar(int, m_iDeathPose ); |
|
CNetworkVar(int, m_iDeathFrame ); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( cs_ragdoll, CCSRagdoll ); |
|
|
|
IMPLEMENT_SERVERCLASS_ST_NOBASE( CCSRagdoll, DT_CSRagdoll ) |
|
SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ), |
|
SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ), |
|
SendPropEHandle( SENDINFO( m_hPlayer ) ), |
|
SendPropModelIndex( SENDINFO( m_nModelIndex ) ), |
|
SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ), |
|
SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ), |
|
SendPropVector( SENDINFO( m_vecRagdollVelocity ) ), |
|
SendPropInt( SENDINFO( m_iDeathPose ), ANIMATION_SEQUENCE_BITS, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_iDeathFrame ), 5 ), |
|
SendPropInt( SENDINFO(m_iTeamNum), TEAMNUM_NUM_BITS, 0), |
|
SendPropInt( SENDINFO( m_bClientSideAnimation ), 1, SPROP_UNSIGNED ), |
|
END_SEND_TABLE() |
|
|
|
|
|
// -------------------------------------------------------------------------------- // |
|
// Player animation event. Sent to the client when a player fires, jumps, reloads, etc.. |
|
// -------------------------------------------------------------------------------- // |
|
|
|
class CTEPlayerAnimEvent : public CBaseTempEntity |
|
{ |
|
public: |
|
DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity ); |
|
DECLARE_SERVERCLASS(); |
|
|
|
CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name ) |
|
{ |
|
} |
|
|
|
CNetworkHandle( CBasePlayer, m_hPlayer ); |
|
CNetworkVar( int, m_iEvent ); |
|
CNetworkVar( int, m_nData ); |
|
}; |
|
|
|
IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent ) |
|
SendPropEHandle( SENDINFO( m_hPlayer ) ), |
|
SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_nData ), 32 ) |
|
END_SEND_TABLE() |
|
|
|
static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" ); |
|
|
|
void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData ) |
|
{ |
|
CPVSFilter filter( (const Vector&)pPlayer->EyePosition() ); |
|
|
|
g_TEPlayerAnimEvent.m_hPlayer = pPlayer; |
|
g_TEPlayerAnimEvent.m_iEvent = event; |
|
g_TEPlayerAnimEvent.m_nData = nData; |
|
g_TEPlayerAnimEvent.Create( filter, 0 ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Filters updates to a variable so that only non-local players see |
|
// the changes. This is so we can send a low-res origin to non-local players |
|
// while sending a hi-res one to the local player. |
|
// Input : *pVarData - |
|
// *pOut - |
|
// objectID - |
|
//----------------------------------------------------------------------------- |
|
|
|
void* SendProxy_SendNonLocalDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID ) |
|
{ |
|
pRecipients->SetAllRecipients(); |
|
pRecipients->ClearRecipient( objectID - 1 ); |
|
return ( void * )pVarData; |
|
} |
|
REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendNonLocalDataTable ); |
|
|
|
|
|
// -------------------------------------------------------------------------------- // |
|
// Tables. |
|
// -------------------------------------------------------------------------------- // |
|
|
|
LINK_ENTITY_TO_CLASS( player, CCSPlayer ); |
|
PRECACHE_REGISTER(player); |
|
|
|
BEGIN_SEND_TABLE_NOBASE( CCSPlayer, DT_CSLocalPlayerExclusive ) |
|
SendPropFloat( SENDINFO( m_flStamina ), 14, 0, 0, 1400 ), |
|
SendPropInt( SENDINFO( m_iDirection ), 1, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_iShotsFired ), 8, SPROP_UNSIGNED ), |
|
SendPropFloat( SENDINFO( m_flVelocityModifier ), 8, 0, 0, 1 ), |
|
|
|
// send a hi-res origin to the local player for use in prediction |
|
SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ), |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj]Set up the send table for per-client domination data |
|
//============================================================================= |
|
|
|
SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominated ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominated ) ) ), |
|
SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominatingMe ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominatingMe ) ) ), |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
END_SEND_TABLE() |
|
|
|
|
|
BEGIN_SEND_TABLE_NOBASE( CCSPlayer, DT_CSNonLocalPlayerExclusive ) |
|
// send a lo-res origin to other players |
|
SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ), |
|
END_SEND_TABLE() |
|
|
|
|
|
IMPLEMENT_SERVERCLASS_ST( CCSPlayer, DT_CSPlayer ) |
|
SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ), |
|
SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ), |
|
SendPropExclude( "DT_BaseAnimating", "m_nSequence" ), |
|
SendPropExclude( "DT_BaseAnimating", "m_nNewSequenceParity" ), |
|
SendPropExclude( "DT_BaseAnimating", "m_nResetEventsParity" ), |
|
SendPropExclude( "DT_BaseAnimating", "m_nMuzzleFlashParity" ), |
|
SendPropExclude( "DT_BaseEntity", "m_angRotation" ), |
|
SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ), |
|
|
|
// cs_playeranimstate and clientside animation takes care of these on the client |
|
SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ), |
|
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), |
|
|
|
// We need to send a hi-res origin to the local player to avoid prediction errors sliding along walls |
|
SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ), |
|
|
|
// Data that only gets sent to the local player. |
|
SendPropDataTable( "cslocaldata", 0, &REFERENCE_SEND_TABLE(DT_CSLocalPlayerExclusive), SendProxy_SendLocalDataTable ), |
|
SendPropDataTable( "csnonlocaldata", 0, &REFERENCE_SEND_TABLE(DT_CSNonLocalPlayerExclusive), SendProxy_SendNonLocalDataTable ), |
|
|
|
SendPropInt( SENDINFO( m_iThrowGrenadeCounter ), THROWGRENADE_COUNTER_BITS, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_iAddonBits ), NUM_ADDON_BITS, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_iPrimaryAddon ), 8, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_iSecondaryAddon ), 8, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_iPlayerState ), Q_log2( NUM_PLAYER_STATES )+1, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_iAccount ), 16, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_bInBombZone ), 1, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_bInBuyZone ), 1, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_iClass ), Q_log2( CS_NUM_CLASSES )+1, SPROP_UNSIGNED ), |
|
SendPropInt( SENDINFO( m_ArmorValue ), 8 ), |
|
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ), |
|
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ), |
|
SendPropBool( SENDINFO( m_bHasDefuser ) ), |
|
SendPropBool( SENDINFO( m_bNightVisionOn ) ), //send as int so we can use a RecvProxy on the client |
|
SendPropBool( SENDINFO( m_bHasNightVision ) ), |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Added for fun-fact support |
|
//============================================================================= |
|
|
|
//SendPropBool( SENDINFO( m_bPickedUpDefuser ) ), |
|
//SendPropBool( SENDINFO( m_bDefusedWithPickedUpKit) ), |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
SendPropBool( SENDINFO( m_bInHostageRescueZone ) ), |
|
SendPropBool( SENDINFO( m_bIsDefusing ) ), |
|
|
|
SendPropBool( SENDINFO( m_bResumeZoom ) ), |
|
SendPropInt( SENDINFO( m_iLastZoom ), 8, SPROP_UNSIGNED ), |
|
|
|
#ifdef CS_SHIELD_ENABLED |
|
SendPropBool( SENDINFO( m_bHasShield ) ), |
|
SendPropBool( SENDINFO( m_bShieldDrawn ) ), |
|
#endif |
|
SendPropBool( SENDINFO( m_bHasHelmet ) ), |
|
SendPropFloat (SENDINFO(m_flFlashDuration), 0, SPROP_NOSCALE ), |
|
SendPropFloat( SENDINFO(m_flFlashMaxAlpha), 0, SPROP_NOSCALE ), |
|
SendPropInt( SENDINFO( m_iProgressBarDuration ), 4, SPROP_UNSIGNED ), |
|
SendPropFloat( SENDINFO( m_flProgressBarStartTime ), 0, SPROP_NOSCALE ), |
|
SendPropEHandle( SENDINFO( m_hRagdoll ) ), |
|
SendPropInt( SENDINFO( m_cycleLatch ), 4, SPROP_UNSIGNED ), |
|
|
|
|
|
END_SEND_TABLE() |
|
|
|
|
|
BEGIN_DATADESC( CCSPlayer ) |
|
|
|
DEFINE_INPUTFUNC( FIELD_VOID, "OnRescueZoneTouch", RescueZoneTouch ), |
|
DEFINE_THINKFUNC( PushawayThink ) |
|
|
|
END_DATADESC() |
|
|
|
|
|
// has to be included after above macros |
|
#include "cs_bot.h" |
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
|
|
// -------------------------------------------------------------------------------- // |
|
|
|
void cc_CreatePredictionError_f( const CCommand &args ) |
|
{ |
|
float distance = 32; |
|
|
|
if ( args.ArgC() >= 2 ) |
|
{ |
|
distance = atof(args[1]); |
|
} |
|
|
|
CBaseEntity *pEnt = CBaseEntity::Instance( 1 ); |
|
pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( distance, 0, 0 ) ); |
|
} |
|
|
|
ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT ); |
|
|
|
|
|
// -------------------------------------------------------------------------------- // |
|
// CCSPlayer implementation. |
|
// -------------------------------------------------------------------------------- // |
|
|
|
CCSPlayer::CCSPlayer() |
|
{ |
|
m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true ); |
|
|
|
UseClientSideAnimation(); |
|
|
|
m_iLastWeaponFireUsercmd = 0; |
|
m_iAddonBits = 0; |
|
m_bEscaped = false; |
|
m_iAccount = 0; |
|
|
|
m_bIsVIP = false; |
|
m_iClass = (int)CS_CLASS_NONE; |
|
m_angEyeAngles.Init(); |
|
|
|
SetViewOffset( VEC_VIEW_SCALED( this ) ); |
|
|
|
m_pCurStateInfo = NULL; // no state yet |
|
m_iThrowGrenadeCounter = 0; |
|
|
|
m_lifeState = LIFE_DEAD; // Start "dead". |
|
m_bInBombZone = false; |
|
m_bInBuyZone = false; |
|
m_bInHostageRescueZone = false; |
|
m_flDeathTime = 0.0f; |
|
m_iHostagesKilled = 0; |
|
iRadioMenu = -1; |
|
m_bTeamChanged = false; |
|
m_iShotsFired = 0; |
|
m_iDirection = 0; |
|
m_receivesMoneyNextRound = true; |
|
m_bIsBeingGivenItem = false; |
|
m_isVIP = false; |
|
|
|
m_bJustKilledTeammate = false; |
|
m_bPunishedForTK = false; |
|
m_iTeamKills = 0; |
|
m_flLastMovement = gpGlobals->curtime; |
|
m_iNextTimeCheck = 0; |
|
|
|
m_szNewName[0] = 0; |
|
m_szClanTag[0] = 0; |
|
|
|
for ( int i=0; i<NAME_CHANGE_HISTORY_SIZE; i++ ) |
|
{ |
|
m_flNameChangeHistory[i] = -NAME_CHANGE_HISTORY_INTERVAL; |
|
} |
|
|
|
m_iIgnoreGlobalChat = 0; |
|
m_bIgnoreRadio = false; |
|
|
|
m_pHintMessageQueue = new CHintMessageQueue(this); |
|
m_iDisplayHistoryBits = 0; |
|
m_bShowHints = true; |
|
m_flNextMouseoverUpdate = gpGlobals->curtime; |
|
|
|
m_lastDamageHealth = 0; |
|
m_lastDamageArmor = 0; |
|
|
|
m_applyDeafnessTime = 0.0f; |
|
|
|
m_cycleLatch = 0; |
|
m_cycleLatchTimer.Invalidate(); |
|
|
|
m_iShouldHaveCash = 0; |
|
|
|
m_lastNavArea = NULL; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Init achievement variables |
|
// [menglish] Init bullet collision variables |
|
//============================================================================= |
|
|
|
m_NumEnemiesKilledThisRound = 0; |
|
m_NumEnemiesAtRoundStart = 0; |
|
m_KillingSpreeStartTime = -1; |
|
m_firstKillBlindStartTime = -1; |
|
m_killsWhileBlind = 0; |
|
m_bSurvivedHeadshotDueToHelmet = false; |
|
m_pGooseChaseDistractingPlayer = NULL; |
|
m_gooseChaseStep = GC_NONE; |
|
m_defuseDefenseStep = DD_NONE; |
|
m_lastRoundResult = Invalid_Round_End_Reason; |
|
m_bMadeFootstepNoise = false; |
|
m_bombPickupTime = -1; |
|
m_bMadePurchseThisRound = false; |
|
m_roundsWonWithoutPurchase = 0; |
|
m_iDeathFlags = 0; |
|
m_lastFlashBangAttacker = NULL; |
|
m_iMVPs = 0; |
|
m_bKilledDefuser = false; |
|
m_bKilledRescuer = false; |
|
m_maxGrenadeKills = 0; |
|
m_grenadeDamageTakenThisRound = 0; |
|
|
|
m_vLastHitLocationObjectSpace = Vector(0,0,0); |
|
|
|
m_wasNotKilledNaturally = false; |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
} |
|
|
|
|
|
CCSPlayer::~CCSPlayer() |
|
{ |
|
delete m_pHintMessageQueue; |
|
m_pHintMessageQueue = NULL; |
|
|
|
// delete the records of damage taken and given |
|
ResetDamageCounters(); |
|
m_PlayerAnimState->Release(); |
|
} |
|
|
|
|
|
CCSPlayer *CCSPlayer::CreatePlayer( const char *className, edict_t *ed ) |
|
{ |
|
CCSPlayer::s_PlayerEdict = ed; |
|
return (CCSPlayer*)CreateEntityByName( className ); |
|
} |
|
|
|
|
|
void CCSPlayer::Precache() |
|
{ |
|
Vector mins( -13, -13, -10 ); |
|
Vector maxs( 13, 13, 75 ); |
|
|
|
int i; |
|
for ( i=0; i<CTPlayerModels.Count(); ++i ) |
|
{ |
|
PrecacheModel( CTPlayerModels[i] ); |
|
engine->ForceModelBounds( CTPlayerModels[i], mins, maxs ); |
|
} |
|
for ( i=0; i<TerroristPlayerModels.Count(); ++i ) |
|
{ |
|
PrecacheModel( TerroristPlayerModels[i] ); |
|
engine->ForceModelBounds( TerroristPlayerModels[i], mins, maxs ); |
|
} |
|
|
|
// Sigh - have to force identical VMTs for the player models. I'm just going to hard-code these |
|
// strings here, rather than have char***'s or the CUtlVector<CUtlVector<>> equivalent. |
|
engine->ForceSimpleMaterial( "materials/models/player/ct_urban/ct_urban.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/ct_urban/ct_urban_glass.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/ct_sas/ct_sas.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/ct_sas/ct_sas_glass.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/ct_gsg9/ct_gsg9.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/ct_gign/ct_gign.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/ct_gign/ct_gign_glass.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/t_phoenix/t_phoenix.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/t_guerilla/t_guerilla.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/t_leet/t_leet.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/t_leet/t_leet_glass.vmt" ); |
|
engine->ForceSimpleMaterial( "materials/models/player/t_arctic/t_arctic.vmt" ); |
|
|
|
#ifdef CS_SHIELD_ENABLED |
|
PrecacheModel( SHIELD_VIEW_MODEL ); |
|
#endif |
|
|
|
PrecacheScriptSound( "Player.DeathHeadShot" ); |
|
PrecacheScriptSound( "Player.Death" ); |
|
PrecacheScriptSound( "Player.DamageHelmet" ); |
|
PrecacheScriptSound( "Player.DamageHeadShot" ); |
|
PrecacheScriptSound( "Flesh.BulletImpact" ); |
|
PrecacheScriptSound( "Player.DamageKevlar" ); |
|
PrecacheScriptSound( "Player.PickupWeapon" ); |
|
PrecacheScriptSound( "Player.NightVisionOff" ); |
|
PrecacheScriptSound( "Player.NightVisionOn" ); |
|
PrecacheScriptSound( "Player.FlashlightOn" ); |
|
PrecacheScriptSound( "Player.FlashlightOff" ); |
|
|
|
// CS Bot sounds |
|
PrecacheScriptSound( "Bot.StuckSound" ); |
|
PrecacheScriptSound( "Bot.StuckStart" ); |
|
PrecacheScriptSound( "Bot.FellOff" ); |
|
|
|
UTIL_PrecacheOther( "item_kevlar" ); |
|
UTIL_PrecacheOther( "item_assaultsuit" ); |
|
UTIL_PrecacheOther( "item_defuser" ); |
|
|
|
PrecacheModel ( "sprites/glow01.vmt" ); |
|
PrecacheModel ( "models/items/cs_gift.mdl" ); |
|
|
|
BaseClass::Precache(); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Allow pre-frame adjustments on the player |
|
//----------------------------------------------------------------------------- |
|
ConVar sv_runcmds( "sv_runcmds", "1" ); |
|
void CCSPlayer::PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper ) |
|
{ |
|
VPROF( "CCSPlayer::PlayerRunCommand" ); |
|
|
|
if ( !sv_runcmds.GetInt() ) |
|
return; |
|
|
|
// don't run commands in the future |
|
if ( !IsEngineThreaded() && |
|
( ucmd->tick_count > (gpGlobals->tickcount + sv_max_usercmd_future_ticks.GetInt()) ) ) |
|
{ |
|
DevMsg( "Client cmd out of sync (delta %i).\n", ucmd->tick_count - gpGlobals->tickcount ); |
|
return; |
|
} |
|
|
|
// If they use a negative bot_mimic value, then don't process their usercmds, but have |
|
// bots process them instead (so they can stay still and have the bot move around). |
|
CUserCmd tempCmd; |
|
if ( -bot_mimic.GetInt() == entindex() ) |
|
{ |
|
tempCmd = *ucmd; |
|
ucmd = &tempCmd; |
|
|
|
ucmd->forwardmove = ucmd->sidemove = ucmd->upmove = 0; |
|
ucmd->buttons = 0; |
|
ucmd->impulse = 0; |
|
} |
|
|
|
if ( IsBot() && bot_crouch.GetInt() ) |
|
ucmd->buttons |= IN_DUCK; |
|
|
|
BaseClass::PlayerRunCommand( ucmd, moveHelper ); |
|
} |
|
|
|
|
|
bool CCSPlayer::RunMimicCommand( CUserCmd& cmd ) |
|
{ |
|
if ( !IsBot() ) |
|
return false; |
|
|
|
int iMimic = abs( bot_mimic.GetInt() ); |
|
if ( iMimic > gpGlobals->maxClients ) |
|
return false; |
|
|
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex( iMimic ); |
|
if ( !pPlayer ) |
|
return false; |
|
|
|
if ( !pPlayer->GetLastUserCommand() ) |
|
return false; |
|
|
|
cmd = *pPlayer->GetLastUserCommand(); |
|
cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat(); |
|
|
|
pl.fixangle = FIXANGLE_NONE; |
|
|
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Simulates a single frame of movement for a player |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::RunPlayerMove( const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime ) |
|
{ |
|
CUserCmd cmd; |
|
|
|
// Store off the globals.. they're gonna get whacked |
|
float flOldFrametime = gpGlobals->frametime; |
|
float flOldCurtime = gpGlobals->curtime; |
|
|
|
float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime; |
|
this->SetTimeBase( flTimeBase ); |
|
|
|
CUserCmd lastUserCmd = *GetLastUserCommand(); |
|
Q_memset( &cmd, 0, sizeof( cmd ) ); |
|
|
|
if ( !RunMimicCommand( cmd ) ) |
|
{ |
|
cmd.forwardmove = forwardmove; |
|
cmd.sidemove = sidemove; |
|
cmd.upmove = upmove; |
|
cmd.buttons = buttons; |
|
cmd.impulse = impulse; |
|
|
|
VectorCopy( viewangles, cmd.viewangles ); |
|
cmd.random_seed = random->RandomInt( 0, 0x7fffffff ); |
|
} |
|
|
|
MoveHelperServer()->SetHost( this ); |
|
PlayerRunCommand( &cmd, MoveHelperServer() ); |
|
|
|
// save off the last good usercmd |
|
if ( -bot_mimic.GetInt() == entindex() ) |
|
{ |
|
CUserCmd lastCmd = *GetLastUserCommand(); |
|
lastCmd.command_number = cmd.command_number; |
|
lastCmd.tick_count = cmd.tick_count; |
|
SetLastUserCommand( lastCmd ); |
|
} |
|
else |
|
{ |
|
SetLastUserCommand( cmd ); |
|
} |
|
|
|
// Clear out any fixangle that has been set |
|
pl.fixangle = FIXANGLE_NONE; |
|
|
|
// Restore the globals.. |
|
gpGlobals->frametime = flOldFrametime; |
|
gpGlobals->curtime = flOldCurtime; |
|
|
|
MoveHelperServer()->SetHost( NULL ); |
|
} |
|
|
|
|
|
void CCSPlayer::InitialSpawn( void ) |
|
{ |
|
BaseClass::InitialSpawn(); |
|
|
|
// we're going to give the bots money here instead of FinishClientPutInServer() |
|
// because of the bots' timing for purchasing weapons/items. |
|
if ( IsBot() ) |
|
{ |
|
m_iAccount = CSGameRules()->GetStartMoney(); |
|
} |
|
|
|
if ( !engine->IsDedicatedServer() && TheNavMesh->IsOutOfDate() && this == UTIL_GetListenServerHost() ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "The Navigation Mesh was built using a different version of this map." ); |
|
} |
|
|
|
State_Enter( STATE_WELCOME ); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj] We reset the stats at the beginning of the map (including domination tracking) |
|
//============================================================================= |
|
|
|
CCS_GameStats.ResetPlayerStats(this); |
|
RemoveNemesisRelationships(); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
} |
|
|
|
void CCSPlayer::SetModelFromClass( void ) |
|
{ |
|
if ( GetTeamNumber() == TEAM_TERRORIST ) |
|
{ |
|
int index = m_iClass - FIRST_T_CLASS; |
|
if ( index < 0 || index >= TerroristPlayerModels.Count() ) |
|
{ |
|
index = RandomInt( 0, TerroristPlayerModels.Count() - 1 ); |
|
m_iClass = index + FIRST_T_CLASS; // clean up players who selected a higher class than we support yet |
|
} |
|
SetModel( TerroristPlayerModels[index] ); |
|
} |
|
else if ( GetTeamNumber() == TEAM_CT ) |
|
{ |
|
int index = m_iClass - FIRST_CT_CLASS; |
|
if ( index < 0 || index >= CTPlayerModels.Count() ) |
|
{ |
|
index = RandomInt( 0, CTPlayerModels.Count() - 1 ); |
|
m_iClass = index + FIRST_CT_CLASS; // clean up players who selected a higher class than we support yet |
|
} |
|
SetModel( CTPlayerModels[index] ); |
|
} |
|
else |
|
{ |
|
SetModel( CTPlayerModels[0] ); |
|
} |
|
} |
|
|
|
void CCSPlayer::Spawn() |
|
{ |
|
m_RateLimitLastCommandTimes.Purge(); |
|
|
|
// Get rid of the progress bar... |
|
SetProgressBarTime( 0 ); |
|
|
|
CreateViewModel( 1 ); |
|
|
|
// Set their player model. |
|
SetModelFromClass(); |
|
|
|
BaseClass::Spawn(); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [pfreese] Clear the last known nav area (used to be done by CBasePlayer) |
|
//============================================================================= |
|
|
|
m_lastNavArea = NULL; |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round. |
|
|
|
// Override what CBasePlayer set for the view offset. |
|
SetViewOffset( VEC_VIEW_SCALED( this ) ); |
|
|
|
// |
|
// Our player movement speed is set once here. This will override the cl_xxxx |
|
// cvars unless they are set to be lower than this. |
|
// |
|
SetMaxSpeed( CS_PLAYER_SPEED_RUN ); |
|
|
|
SetFOV( this, 0 ); |
|
|
|
m_bIsDefusing = false; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN |
|
// [dwenger] Reset hostage-related variables |
|
//============================================================================= |
|
|
|
m_bIsRescuing = false; |
|
m_bInjuredAHostage = false; |
|
m_iNumFollowers = 0; |
|
|
|
// [tj] Reset this flag if the player is not in observer mode (as happens when a player spawns late) |
|
if (m_iPlayerState != STATE_OBSERVER_MODE) |
|
{ |
|
m_wasNotKilledNaturally = false; |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
m_iShotsFired = 0; |
|
m_iDirection = 0; |
|
|
|
if ( m_pHintMessageQueue ) |
|
{ |
|
m_pHintMessageQueue->Reset(); |
|
} |
|
m_iDisplayHistoryBits &= ~DHM_ROUND_CLEAR; |
|
|
|
// Special-case here. A bunch of things happen in CBasePlayer::Spawn(), and we really want the |
|
// player states to control these things, so give whatever player state we're in a chance |
|
// to reinitialize itself. |
|
State_Transition( m_iPlayerState ); |
|
|
|
ClearFlashbangScreenFade(); |
|
|
|
m_flVelocityModifier = 1.0f; |
|
|
|
ResetStamina(); |
|
|
|
m_flLastRadarUpdateTime = 0.0f; |
|
|
|
m_iNumSpawns++; |
|
|
|
if ( !engine->IsDedicatedServer() && CSGameRules()->m_iTotalRoundsPlayed < 2 && TheNavMesh->IsOutOfDate() && this == UTIL_GetListenServerHost() ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "The Navigation Mesh was built using a different version of this map." ); |
|
} |
|
|
|
m_bTeamChanged = false; |
|
m_iOldTeam = TEAM_UNASSIGNED; |
|
|
|
m_iRadioMessages = 60; |
|
m_flRadioTime = gpGlobals->curtime; |
|
|
|
if ( m_hRagdoll ) |
|
{ |
|
UTIL_Remove( m_hRagdoll ); |
|
} |
|
|
|
m_hRagdoll = NULL; |
|
|
|
// did we change our name while we were dead? |
|
if ( m_szNewName[0] != 0 ) |
|
{ |
|
ChangeName( m_szNewName ); |
|
m_szNewName[0] = 0; |
|
} |
|
|
|
if ( m_bIsVIP ) |
|
{ |
|
HintMessage( "#Hint_you_are_the_vip", true, true ); |
|
} |
|
|
|
m_bIsInAutoBuy = false; |
|
m_bIsInRebuy = false; |
|
m_bAutoReload = false; |
|
|
|
SetContextThink( &CCSPlayer::PushawayThink, gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, CS_PUSHAWAY_THINK_CONTEXT ); |
|
|
|
if ( GetActiveWeapon() && !IsObserver() ) |
|
{ |
|
GetActiveWeapon()->Deploy(); |
|
m_flNextAttack = gpGlobals->curtime; // Allow reloads to finish, since we're playing the deploy anim instead. This mimics goldsrc behavior, anyway. |
|
} |
|
|
|
m_applyDeafnessTime = 0.0f; |
|
|
|
m_cycleLatch = 0; |
|
m_cycleLatchTimer.Start( RandomFloat( 0.0f, CycleLatchInterval ) ); |
|
|
|
StockPlayerAmmo(); |
|
} |
|
|
|
void CCSPlayer::ShowViewPortPanel( const char * name, bool bShow, KeyValues *data ) |
|
{ |
|
if ( CSGameRules()->IsLogoMap() ) |
|
return; |
|
|
|
if ( CommandLine()->FindParm("-makedevshots") ) |
|
return; |
|
|
|
BaseClass::ShowViewPortPanel( name, bShow, data ); |
|
} |
|
|
|
void CCSPlayer::ClearFlashbangScreenFade( void ) |
|
{ |
|
if( IsBlind() ) |
|
{ |
|
color32 clr = { 0, 0, 0, 0 }; |
|
UTIL_ScreenFade( this, clr, 0.01, 0.0, FFADE_OUT | FFADE_PURGE ); |
|
|
|
m_flFlashDuration = 0.0f; |
|
m_flFlashMaxAlpha = 255.0f; |
|
} |
|
|
|
// clear blind time (after screen fades are canceled) |
|
m_blindUntilTime = 0.0f; |
|
m_blindStartTime = 0.0f; |
|
} |
|
|
|
void CCSPlayer::GiveDefaultItems() |
|
{ |
|
// Always give the player the knife. |
|
CBaseCombatWeapon *pistol = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
if ( pistol ) |
|
{ |
|
return; |
|
} |
|
|
|
m_bUsingDefaultPistol = true; |
|
|
|
if ( GetTeamNumber() == TEAM_CT ) |
|
{ |
|
GiveNamedItem( "weapon_knife" ); |
|
GiveNamedItem( "weapon_usp" ); |
|
GiveAmmo( 24, BULLET_PLAYER_45ACP ); |
|
} |
|
else if ( GetTeamNumber() == TEAM_TERRORIST ) |
|
{ |
|
GiveNamedItem( "weapon_knife" ); |
|
GiveNamedItem( "weapon_glock" ); |
|
GiveAmmo( 40, BULLET_PLAYER_9MM ); |
|
} |
|
} |
|
|
|
void CCSPlayer::SetClanTag( const char *pTag ) |
|
{ |
|
if ( pTag ) |
|
{ |
|
Q_strncpy( m_szClanTag, pTag, sizeof( m_szClanTag ) ); |
|
} |
|
} |
|
void CCSPlayer::CreateRagdollEntity() |
|
{ |
|
// If we already have a ragdoll, don't make another one. |
|
CCSRagdoll *pRagdoll = dynamic_cast< CCSRagdoll* >( m_hRagdoll.Get() ); |
|
|
|
if ( !pRagdoll ) |
|
{ |
|
// create a new one |
|
pRagdoll = dynamic_cast< CCSRagdoll* >( CreateEntityByName( "cs_ragdoll" ) ); |
|
} |
|
|
|
if ( pRagdoll ) |
|
{ |
|
pRagdoll->m_hPlayer = this; |
|
pRagdoll->m_vecRagdollOrigin = GetAbsOrigin(); |
|
pRagdoll->m_vecRagdollVelocity = GetAbsVelocity(); |
|
pRagdoll->m_nModelIndex = m_nModelIndex; |
|
pRagdoll->m_nForceBone = m_nForceBone; |
|
pRagdoll->m_vecForce = m_vecTotalBulletForce; |
|
pRagdoll->m_iDeathPose = m_iDeathPose; |
|
pRagdoll->m_iDeathFrame = m_iDeathFrame; |
|
pRagdoll->Init(); |
|
} |
|
|
|
// ragdolls will be removed on round restart automatically |
|
m_hRagdoll = pRagdoll; |
|
} |
|
|
|
int CCSPlayer::OnTakeDamage_Alive( const CTakeDamageInfo &info ) |
|
{ |
|
// set damage type sustained |
|
m_bitsDamageType |= info.GetDamageType(); |
|
|
|
if ( !CBaseCombatCharacter::OnTakeDamage_Alive( info ) ) |
|
return 0; |
|
|
|
// don't apply damage forces in CS |
|
|
|
// fire global game event |
|
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" ); |
|
|
|
if ( event ) |
|
{ |
|
event->SetInt("userid", GetUserID() ); |
|
event->SetInt("health", MAX(0, m_iHealth) ); |
|
event->SetInt("armor", MAX(0, ArmorValue()) ); |
|
|
|
event->SetInt( "dmg_health", m_lastDamageHealth ); |
|
event->SetInt( "dmg_armor", m_lastDamageArmor ); |
|
|
|
if ( info.GetDamageType() & DMG_BLAST ) |
|
{ |
|
event->SetInt( "hitgroup", HITGROUP_GENERIC ); |
|
} |
|
else |
|
{ |
|
event->SetInt( "hitgroup", m_LastHitGroup ); |
|
} |
|
|
|
CBaseEntity * attacker = info.GetAttacker(); |
|
const char *weaponName = ""; |
|
|
|
if ( attacker->IsPlayer() ) |
|
{ |
|
CBasePlayer *player = ToBasePlayer( attacker ); |
|
event->SetInt("attacker", player->GetUserID() ); // hurt by other player |
|
|
|
CBaseEntity *pInflictor = info.GetInflictor(); |
|
if ( pInflictor ) |
|
{ |
|
if ( pInflictor == player ) |
|
{ |
|
// If the inflictor is the killer, then it must be their current weapon doing the damage |
|
if ( player->GetActiveWeapon() ) |
|
{ |
|
weaponName = player->GetActiveWeapon()->GetClassname(); |
|
} |
|
} |
|
else |
|
{ |
|
weaponName = STRING( pInflictor->m_iClassname ); // it's just that easy |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
event->SetInt("attacker", 0 ); // hurt by "world" |
|
} |
|
|
|
if ( strncmp( weaponName, "weapon_", 7 ) == 0 ) |
|
{ |
|
weaponName += 7; |
|
} |
|
else if( strncmp( weaponName, "hegrenade", 9 ) == 0 ) //"hegrenade_projectile" |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj] Handle grenade-surviving achievement |
|
//============================================================================= |
|
if (info.GetAttacker()->GetTeamNumber() != GetTeamNumber()) |
|
{ |
|
m_grenadeDamageTakenThisRound += info.GetDamage(); |
|
} |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
weaponName = "hegrenade"; |
|
} |
|
else if( strncmp( weaponName, "flashbang", 9 ) == 0 ) //"flashbang_projectile" |
|
{ |
|
weaponName = "flashbang"; |
|
} |
|
else if( strncmp( weaponName, "smokegrenade", 12 ) == 0 ) //"smokegrenade_projectile" |
|
{ |
|
weaponName = "smokegrenade"; |
|
} |
|
|
|
event->SetString( "weapon", weaponName ); |
|
event->SetInt( "priority", 5 ); |
|
|
|
gameeventmanager->FireEvent( event ); |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Supports fun-fact |
|
//============================================================================= |
|
|
|
// Returns the % of the enemies this player killed in the round |
|
int CCSPlayer::GetPercentageOfEnemyTeamKilled() |
|
{ |
|
if ( m_NumEnemiesAtRoundStart > 0 ) |
|
{ |
|
return (int)( ( (float)m_NumEnemiesKilledThisRound / (float)m_NumEnemiesAtRoundStart ) * 100.0f ); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
void CCSPlayer::Event_Killed( const CTakeDamageInfo &info ) |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [pfreese] Process on-death achievements |
|
//============================================================================= |
|
|
|
ProcessPlayerDeathAchievements(ToCSPlayer(info.GetAttacker()), this, info); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
SetArmorValue( 0 ); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj] Added a parameter so we know if it was death that caused the drop |
|
// [menglish] Keep track of what the player has dropped for the freeze panel callouts |
|
//============================================================================= |
|
|
|
CBaseEntity* pAttacker = info.GetAttacker(); |
|
bool friendlyFire = pAttacker && pAttacker->GetTeamNumber() == GetTeamNumber(); |
|
|
|
//Only count the drop if it was not friendly fire |
|
DropWeapons(true, !friendlyFire); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
|
|
// Just in case the progress bar is on screen, kill it. |
|
SetProgressBarTime( 0 ); |
|
|
|
m_bIsDefusing = false; |
|
|
|
m_bHasNightVision = false; |
|
m_bNightVisionOn = false; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Added for fun-fact support |
|
//============================================================================= |
|
|
|
m_bPickedUpDefuser = false; |
|
m_bDefusedWithPickedUpKit = false; |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
m_bHasHelmet = false; |
|
|
|
m_flFlashDuration = 0.0f; |
|
|
|
FlashlightTurnOff(); |
|
|
|
// show killer in death cam mode |
|
if( IsValidObserverTarget( info.GetAttacker() ) ) |
|
{ |
|
SetObserverTarget( info.GetAttacker() ); |
|
} |
|
else |
|
{ |
|
ResetObserverMode(); |
|
} |
|
|
|
//update damage info with our accumulated physics force |
|
CTakeDamageInfo subinfo = info; |
|
|
|
// HACK[pfreese]: scale impulse up for visual effect |
|
const float kImpulseBonusScale = 2.0f; |
|
subinfo.SetDamageForce( m_vecTotalBulletForce * kImpulseBonusScale); |
|
|
|
//Adrian: Select a death pose to extrapolate the ragdoll's velocity. |
|
SelectDeathPose( info ); |
|
|
|
// See if there's a ragdoll magnet that should influence our force. |
|
CRagdollMagnet *pMagnet = CRagdollMagnet::FindBestMagnet( this ); |
|
if( pMagnet ) |
|
{ |
|
m_vecTotalBulletForce += pMagnet->GetForceVector( this ); |
|
} |
|
|
|
// Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW |
|
// because we still want to transmit to the clients in our PVS. |
|
CreateRagdollEntity(); |
|
|
|
// Special code to drop holiday gifts for the holiday achievement |
|
if ( ( mp_holiday_nogifts.GetBool() == false ) && UTIL_IsHolidayActive( 3 /*kHoliday_Christmas*/ ) ) |
|
{ |
|
if ( RandomInt( 0, 100 ) < 20 ) |
|
{ |
|
CHolidayGift::Create( WorldSpaceCenter(), GetAbsAngles(), EyeAngles(), GetAbsVelocity(), this ); |
|
} |
|
} |
|
|
|
State_Transition( STATE_DEATH_ANIM ); // Transition into the dying state. |
|
BaseClass::Event_Killed( subinfo ); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [pfreese] If this kill ended the round, award the MVP to someone on the |
|
// winning team. |
|
// TODO - move this code somewhere else more MVP related |
|
//============================================================================= |
|
|
|
bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE); |
|
bool roundIsWonNow = CSGameRules()->CheckWinConditions(); |
|
|
|
if ( !roundWasAlreadyWon && roundIsWonNow ) |
|
{ |
|
CCSPlayer* pMVP = NULL; |
|
int maxKills = 0; |
|
int maxDamage = 0; |
|
|
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
CCSPlayer* pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) ); |
|
if ( pPlayer ) |
|
{ |
|
// only consider players on the winning team |
|
if ( pPlayer->GetTeamNumber() != CSGameRules()->m_iRoundWinStatus ) |
|
continue; |
|
|
|
int nKills = CCS_GameStats.FindPlayerStats( pPlayer ).statsCurrentRound[CSSTAT_KILLS]; |
|
int nDamage = CCS_GameStats.FindPlayerStats( pPlayer ).statsCurrentRound[CSSTAT_DAMAGE]; |
|
|
|
if ( nKills > maxKills || ( nKills == maxKills && nDamage > maxDamage ) ) |
|
{ |
|
pMVP = pPlayer; |
|
maxKills = nKills; |
|
maxDamage = nDamage; |
|
} |
|
} |
|
} |
|
|
|
if ( pMVP ) |
|
{ |
|
pMVP->IncrementNumMVPs( CSMVP_ELIMINATION ); |
|
} |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
OutputDamageGiven(); |
|
OutputDamageTaken(); |
|
ResetDamageCounters(); |
|
|
|
if ( m_bPunishedForTK ) |
|
{ |
|
m_bPunishedForTK = false; |
|
HintMessage( "#Hint_cannot_play_because_tk", true, true ); |
|
} |
|
|
|
if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_SPEC_DUCK; |
|
HintMessage( "#Spec_Duck", true, true ); |
|
} |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish, tj] Update and check any one-off achievements based on the kill |
|
//============================================================================= |
|
|
|
// Notify that I've killed some other entity. (called from Victim's Event_Killed). |
|
void CCSPlayer::Event_KilledOther( CBaseEntity *pVictim, const CTakeDamageInfo &info ) |
|
{ |
|
BaseClass::Event_KilledOther(pVictim, info); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
void CCSPlayer::DeathSound( const CTakeDamageInfo &info ) |
|
{ |
|
if( m_LastHitGroup == HITGROUP_HEAD ) |
|
{ |
|
EmitSound( "Player.DeathHeadShot" ); |
|
} |
|
else |
|
{ |
|
EmitSound( "Player.Death" ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity ) |
|
{ |
|
BaseClass::InitVCollision( vecAbsOrigin, vecAbsVelocity ); |
|
|
|
if ( sv_turbophysics.GetBool() ) |
|
return; |
|
|
|
// Setup the HL2 specific callback. |
|
GetPhysicsController()->SetEventHandler( &playerCallback ); |
|
} |
|
|
|
void CCSPlayer::VPhysicsShadowUpdate( IPhysicsObject *pPhysics ) |
|
{ |
|
if ( !CanMove() ) |
|
return; |
|
|
|
BaseClass::VPhysicsShadowUpdate( pPhysics ); |
|
} |
|
|
|
bool CCSPlayer::HasShield() const |
|
{ |
|
#ifdef CS_SHIELD_ENABLED |
|
return m_bHasShield; |
|
#else |
|
return false; |
|
#endif |
|
} |
|
|
|
|
|
bool CCSPlayer::IsShieldDrawn() const |
|
{ |
|
#ifdef CS_SHIELD_ENABLED |
|
return m_bShieldDrawn; |
|
#else |
|
return false; |
|
#endif |
|
} |
|
|
|
|
|
void CCSPlayer::CheatImpulseCommands( int iImpulse ) |
|
{ |
|
switch( iImpulse ) |
|
{ |
|
case 101: |
|
{ |
|
if( sv_cheats->GetBool() ) |
|
{ |
|
extern int gEvilImpulse101; |
|
gEvilImpulse101 = true; |
|
|
|
AddAccount( 16000 ); |
|
|
|
GiveAmmo( 250, BULLET_PLAYER_50AE ); |
|
GiveAmmo( 250, BULLET_PLAYER_762MM ); |
|
GiveAmmo( 250, BULLET_PLAYER_338MAG ); |
|
GiveAmmo( 250, BULLET_PLAYER_556MM ); |
|
GiveAmmo( 250, BULLET_PLAYER_556MM_BOX ); |
|
GiveAmmo( 250, BULLET_PLAYER_9MM ); |
|
GiveAmmo( 250, BULLET_PLAYER_BUCKSHOT ); |
|
GiveAmmo( 250, BULLET_PLAYER_45ACP ); |
|
GiveAmmo( 250, BULLET_PLAYER_357SIG ); |
|
GiveAmmo( 250, BULLET_PLAYER_57MM ); |
|
|
|
gEvilImpulse101 = false; |
|
} |
|
} |
|
break; |
|
|
|
default: |
|
{ |
|
BaseClass::CheatImpulseCommands( iImpulse ); |
|
} |
|
} |
|
} |
|
|
|
void CCSPlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize ) |
|
{ |
|
BaseClass::SetupVisibility( pViewEntity, pvs, pvssize ); |
|
|
|
int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum(); |
|
PointCameraSetupVisibility( this, area, pvs, pvssize ); |
|
} |
|
|
|
void CCSPlayer::UpdateAddonBits() |
|
{ |
|
int iNewBits = 0; |
|
|
|
int nFlashbang = GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_FLASHBANG ) ); |
|
if ( dynamic_cast< CFlashbang* >( GetActiveWeapon() ) ) |
|
{ |
|
--nFlashbang; |
|
} |
|
|
|
if ( nFlashbang >= 1 ) |
|
iNewBits |= ADDON_FLASHBANG_1; |
|
|
|
if ( nFlashbang >= 2 ) |
|
iNewBits |= ADDON_FLASHBANG_2; |
|
|
|
if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_HEGRENADE ) ) && |
|
!dynamic_cast< CHEGrenade* >( GetActiveWeapon() ) ) |
|
{ |
|
iNewBits |= ADDON_HE_GRENADE; |
|
} |
|
|
|
if ( GetAmmoCount( GetAmmoDef()->Index( AMMO_TYPE_SMOKEGRENADE ) ) && |
|
!dynamic_cast< CSmokeGrenade* >( GetActiveWeapon() ) ) |
|
{ |
|
iNewBits |= ADDON_SMOKE_GRENADE; |
|
} |
|
|
|
if ( HasC4() && !dynamic_cast< CC4* >( GetActiveWeapon() ) ) |
|
iNewBits |= ADDON_C4; |
|
|
|
if ( HasDefuser() ) |
|
iNewBits |= ADDON_DEFUSEKIT; |
|
|
|
CWeaponCSBase *weapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE )); |
|
if ( weapon && weapon != GetActiveWeapon() ) |
|
{ |
|
iNewBits |= ADDON_PRIMARY; |
|
m_iPrimaryAddon = weapon->GetWeaponID(); |
|
} |
|
else |
|
{ |
|
m_iPrimaryAddon = WEAPON_NONE; |
|
} |
|
|
|
weapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL )); |
|
if ( weapon && weapon != GetActiveWeapon() ) |
|
{ |
|
iNewBits |= ADDON_PISTOL; |
|
if ( weapon->GetWeaponID() == WEAPON_ELITE ) |
|
{ |
|
iNewBits |= ADDON_PISTOL2; |
|
} |
|
m_iSecondaryAddon = weapon->GetWeaponID(); |
|
} |
|
else if ( weapon && weapon->GetWeaponID() == WEAPON_ELITE ) |
|
{ |
|
// The active weapon is weapon_elite. Set ADDON_PISTOL2 without ADDON_PISTOL, so we know |
|
// to display the empty holster. |
|
iNewBits |= ADDON_PISTOL2; |
|
m_iSecondaryAddon = weapon->GetWeaponID(); |
|
} |
|
else |
|
{ |
|
m_iSecondaryAddon = WEAPON_NONE; |
|
} |
|
|
|
m_iAddonBits = iNewBits; |
|
} |
|
|
|
void CCSPlayer::UpdateRadar() |
|
{ |
|
// update once a second |
|
if ( (m_flLastRadarUpdateTime + 1.0) > gpGlobals->curtime ) |
|
return; |
|
|
|
m_flLastRadarUpdateTime = gpGlobals->curtime; |
|
|
|
// update positions of all players outside of my PVS |
|
CBitVec< ABSOLUTE_PLAYER_LIMIT > playerbits; |
|
engine->Message_DetermineMulticastRecipients( false, EyePosition(), playerbits ); |
|
|
|
CSingleUserRecipientFilter user( this ); |
|
UserMessageBegin( user, "UpdateRadar" ); |
|
|
|
for ( int i=0; i < MAX_PLAYERS; i++ ) |
|
{ |
|
CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i+1 ) ); |
|
|
|
if ( !pPlayer ) |
|
continue; // nothing there |
|
|
|
bool bSameTeam = pPlayer->GetTeamNumber() == GetTeamNumber(); |
|
|
|
if ( playerbits.Get(i) && bSameTeam == true ) |
|
continue; // this player is in my PVS and not in my team, don't update radar pos |
|
|
|
if ( pPlayer == this ) |
|
continue; |
|
|
|
if ( !pPlayer->IsAlive() || pPlayer->IsObserver() || !pPlayer->IsConnected() ) |
|
continue; // don't update specattors or dead players |
|
|
|
WRITE_BYTE( i+1 ); // player index as entity |
|
WRITE_SBITLONG( pPlayer->GetAbsOrigin().x/4, COORD_INTEGER_BITS-1 ); |
|
WRITE_SBITLONG( pPlayer->GetAbsOrigin().y/4, COORD_INTEGER_BITS-1 ); |
|
WRITE_SBITLONG( pPlayer->GetAbsOrigin().z/4, COORD_INTEGER_BITS-1 ); |
|
WRITE_SBITLONG( AngleNormalize( pPlayer->GetAbsAngles().y ), 9 ); |
|
} |
|
|
|
WRITE_BYTE( 0 ); // end marker |
|
|
|
MessageEnd(); |
|
} |
|
|
|
void CCSPlayer::UpdateMouseoverHints() |
|
{ |
|
if ( IsBlind() || IsObserver() ) |
|
return; |
|
|
|
Vector forward, up; |
|
EyeVectors( &forward, NULL, &up ); |
|
|
|
trace_t tr; |
|
// Search for objects in a sphere (tests for entities that are not solid, yet still useable) |
|
Vector searchStart = EyePosition(); |
|
Vector searchEnd = searchStart + forward * 2048; |
|
|
|
int useableContents = MASK_NPCSOLID_BRUSHONLY | MASK_VISIBLE_AND_NPCS; |
|
|
|
UTIL_TraceLine( searchStart, searchEnd, useableContents, this, COLLISION_GROUP_NONE, &tr ); |
|
|
|
if ( tr.fraction != 1.0f ) |
|
{ |
|
if (tr.DidHitNonWorldEntity() && tr.m_pEnt) |
|
{ |
|
CBaseEntity *pObject = tr.m_pEnt; |
|
switch ( pObject->Classify() ) |
|
{ |
|
case CLASS_PLAYER: |
|
{ |
|
const float grenadeBloat = 1.2f; // Be conservative in estimating what a player can distinguish |
|
if ( !TheBots->IsLineBlockedBySmoke( EyePosition(), pObject->EyePosition(), grenadeBloat ) ) |
|
{ |
|
if ( g_pGameRules->PlayerRelationship( this, pObject ) == GR_TEAMMATE ) |
|
{ |
|
if ( !(m_iDisplayHistoryBits & DHF_FRIEND_SEEN) ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_FRIEND_SEEN; |
|
HintMessage( "#Hint_spotted_a_friend", true ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( !(m_iDisplayHistoryBits & DHF_ENEMY_SEEN) ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_ENEMY_SEEN; |
|
HintMessage( "#Hint_spotted_an_enemy", true ); |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
case CLASS_PLAYER_ALLY: |
|
switch ( GetTeamNumber() ) |
|
{ |
|
case TEAM_CT: |
|
if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR) && tr.fraction > 0.1f ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR; |
|
HintMessage( "#Hint_rescue_the_hostages", true ); |
|
} |
|
else if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_NEAR) && tr.fraction <= 0.1f ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR; |
|
m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_NEAR; |
|
HintMessage( "#Hint_press_use_so_hostage_will_follow", false ); |
|
} |
|
break; |
|
case TEAM_TERRORIST: |
|
if ( !(m_iDisplayHistoryBits & DHF_HOSTAGE_SEEN_FAR) ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_HOSTAGE_SEEN_FAR; |
|
HintMessage( "#Hint_prevent_hostage_rescue", true ); |
|
} |
|
break; |
|
} |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
void CCSPlayer::PostThink() |
|
{ |
|
BaseClass::PostThink(); |
|
|
|
UpdateAddonBits(); |
|
|
|
UpdateRadar(); |
|
|
|
if ( !(m_iDisplayHistoryBits & DHF_ROUND_STARTED) && CanPlayerBuy(false) ) |
|
{ |
|
HintMessage( "#Hint_press_buy_to_purchase", false ); |
|
m_iDisplayHistoryBits |= DHF_ROUND_STARTED; |
|
} |
|
if ( m_flNextMouseoverUpdate < gpGlobals->curtime ) |
|
{ |
|
m_flNextMouseoverUpdate = gpGlobals->curtime + 0.2f; |
|
if ( m_bShowHints ) |
|
{ |
|
UpdateMouseoverHints(); |
|
} |
|
} |
|
if ( GetActiveWeapon() && !(m_iDisplayHistoryBits & DHF_AMMO_EXHAUSTED) ) |
|
{ |
|
CBaseCombatWeapon *pWeapon = GetActiveWeapon(); |
|
if ( !pWeapon->HasAnyAmmo() && !(pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE) ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_AMMO_EXHAUSTED; |
|
HintMessage( "#Hint_out_of_ammo", false ); |
|
} |
|
} |
|
|
|
QAngle angles = GetLocalAngles(); |
|
angles[PITCH] = 0; |
|
SetLocalAngles( angles ); |
|
|
|
// Store the eye angles pitch so the client can compute its animation state correctly. |
|
m_angEyeAngles = EyeAngles(); |
|
|
|
m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] ); |
|
|
|
// check if we need to apply a deafness DSP effect. |
|
if ((m_applyDeafnessTime != 0.0f) && (m_applyDeafnessTime <= gpGlobals->curtime)) |
|
{ |
|
ApplyDeafnessEffect(); |
|
} |
|
|
|
if ( IsPlayerUnderwater() && GetWaterLevel() < 3 ) |
|
{ |
|
StopSound( "Player.AmbientUnderWater" ); |
|
SetPlayerUnderwater( false ); |
|
} |
|
|
|
if( IsAlive() && m_cycleLatchTimer.IsElapsed() ) |
|
{ |
|
m_cycleLatchTimer.Start( CycleLatchInterval ); |
|
|
|
// Cycle is a float from 0 to 1. We don't need to transmit a whole float for that. Compress it in to a small fixed point |
|
m_cycleLatch.GetForModify() = 16 * GetCycle();// 4 point fixed |
|
} |
|
} |
|
|
|
|
|
void CCSPlayer::PushawayThink() |
|
{ |
|
// Push physics props out of our way. |
|
PerformObstaclePushaway( this ); |
|
SetNextThink( gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, CS_PUSHAWAY_THINK_CONTEXT ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns whether or not we can switch to the given weapon. |
|
// Input : pWeapon - |
|
//----------------------------------------------------------------------------- |
|
bool CCSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon ) |
|
{ |
|
if ( !pWeapon->CanDeploy() ) |
|
return false; |
|
|
|
if ( GetActiveWeapon() ) |
|
{ |
|
if ( !GetActiveWeapon()->CanHolster() ) |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool CCSPlayer::ShouldDoLargeFlinch( int nHitGroup, CBaseEntity *pAttacker ) |
|
{ |
|
if ( FBitSet( GetFlags(), FL_DUCKING ) ) |
|
return FALSE; |
|
|
|
if ( nHitGroup == HITGROUP_LEFTLEG ) |
|
return FALSE; |
|
|
|
if ( nHitGroup == HITGROUP_RIGHTLEG ) |
|
return FALSE; |
|
|
|
CCSPlayer *pPlayer = ToCSPlayer( pAttacker ); |
|
|
|
if ( pPlayer == NULL || !pPlayer->IsPlayer() ) |
|
pPlayer = NULL; |
|
|
|
if ( pPlayer ) |
|
{ |
|
CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon(); |
|
|
|
if ( pWeapon ) |
|
{ |
|
if ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_RIFLE || |
|
pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SHOTGUN || |
|
pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SNIPER_RIFLE ) |
|
return true; |
|
} |
|
else |
|
return false; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool CCSPlayer::IsArmored( int nHitGroup ) |
|
{ |
|
bool bApplyArmor = false; |
|
|
|
if ( ArmorValue() > 0 ) |
|
{ |
|
switch ( nHitGroup ) |
|
{ |
|
case HITGROUP_GENERIC: |
|
case HITGROUP_CHEST: |
|
case HITGROUP_STOMACH: |
|
case HITGROUP_LEFTARM: |
|
case HITGROUP_RIGHTARM: |
|
bApplyArmor = true; |
|
break; |
|
case HITGROUP_HEAD: |
|
if ( m_bHasHelmet ) |
|
{ |
|
bApplyArmor = true; |
|
} |
|
break; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
return bApplyArmor; |
|
} |
|
|
|
void CCSPlayer::Pain( bool bHasArmour ) |
|
{ |
|
switch (m_LastHitGroup) |
|
{ |
|
case HITGROUP_HEAD: |
|
if (m_bHasHelmet) // He's wearing a helmet |
|
{ |
|
EmitSound( "Player.DamageHelmet" ); |
|
} |
|
else // He's not wearing a helmet |
|
{ |
|
EmitSound( "Player.DamageHeadShot" ); |
|
} |
|
break; |
|
default: |
|
if ( bHasArmour == false ) |
|
{ |
|
EmitSound( "Flesh.BulletImpact" ); |
|
} |
|
else |
|
{ |
|
EmitSound( "Player.DamageKevlar" ); |
|
} |
|
break; |
|
} |
|
} |
|
|
|
int CCSPlayer::OnTakeDamage( const CTakeDamageInfo &inputInfo ) |
|
{ |
|
CTakeDamageInfo info = inputInfo; |
|
|
|
CBaseEntity *pInflictor = info.GetInflictor(); |
|
|
|
if ( !pInflictor ) |
|
return 0; |
|
|
|
if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER ) |
|
return 0; |
|
|
|
const float flArmorBonus = 0.5f; |
|
float flArmorRatio = 0.5f; |
|
float flDamage = info.GetDamage(); |
|
|
|
bool bFriendlyFire = CSGameRules()->IsFriendlyFireOn(); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj] Added properties for goose chase achievement |
|
//============================================================================= |
|
|
|
CSGameRules()->PlayerTookDamage(this, inputInfo); |
|
|
|
//Check "Goose Chase" achievement |
|
CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker()); |
|
if (m_bIsDefusing && m_gooseChaseStep == GC_NONE && pAttacker && pAttacker->GetTeamNumber() != GetTeamNumber() ) |
|
{ |
|
|
|
//Count enemies |
|
int livingEnemies = 0; |
|
CTeam *pAttackerTeam = GetGlobalTeam( pAttacker->GetTeamNumber() ); |
|
for ( int iPlayer=0; iPlayer < pAttackerTeam->GetNumPlayers(); iPlayer++ ) |
|
{ |
|
CCSPlayer *pPlayer = ToCSPlayer( pAttackerTeam->GetPlayer( iPlayer ) ); |
|
Assert( pPlayer ); |
|
if ( !pPlayer ) |
|
continue; |
|
|
|
Assert( pPlayer->GetTeamNumber() == pAttackerTeam->GetTeamNumber() ); |
|
|
|
if ( pPlayer->m_lifeState == LIFE_ALIVE ) |
|
{ |
|
livingEnemies++; |
|
} |
|
} |
|
|
|
//Must be last enemy alive; |
|
if (livingEnemies == 1) |
|
{ |
|
m_gooseChaseStep = GC_SHOT_DURING_DEFUSE; |
|
m_pGooseChaseDistractingPlayer = pAttacker; |
|
} |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
|
|
// warn about team attacks |
|
if ( bFriendlyFire && pInflictor->GetTeamNumber() == GetTeamNumber() && pInflictor != this && info.GetAttacker() != this ) |
|
{ |
|
CCSPlayer *pCSAttacker = ToCSPlayer( pInflictor ); |
|
if ( !pCSAttacker ) |
|
pCSAttacker = ToCSPlayer( info.GetAttacker() ); |
|
|
|
if ( pCSAttacker ) |
|
{ |
|
if ( !(pCSAttacker->m_iDisplayHistoryBits & DHF_FRIEND_INJURED) ) |
|
{ |
|
pCSAttacker->HintMessage( "#Hint_try_not_to_injure_teammates", false ); |
|
pCSAttacker->m_iDisplayHistoryBits |= DHF_FRIEND_INJURED; |
|
} |
|
|
|
if ( (pCSAttacker->m_flLastAttackedTeammate + 0.6f) < gpGlobals->curtime ) |
|
{ |
|
pCSAttacker->m_flLastAttackedTeammate = gpGlobals->curtime; |
|
|
|
// tell the rest of this player's team |
|
Msg( "%s attacked a teammate\n", pCSAttacker->GetPlayerName() ); |
|
for ( int i=1; i<=gpGlobals->maxClients; ++i ) |
|
{ |
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); |
|
if ( pPlayer && pPlayer->GetTeamNumber() == GetTeamNumber() ) |
|
{ |
|
ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_teammate_attack", pCSAttacker->GetPlayerName() ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ( bFriendlyFire || |
|
pInflictor->GetTeamNumber() != GetTeamNumber() || |
|
pInflictor == this || |
|
info.GetAttacker() == this ) |
|
{ |
|
if ( bFriendlyFire && (info.GetDamageType() & DMG_BLAST) == 0 ) |
|
{ |
|
if ( pInflictor->GetTeamNumber() == GetTeamNumber() ) |
|
{ |
|
flDamage *= 0.35; // bullets hurt teammates less |
|
} |
|
} |
|
|
|
if ( ShouldDoLargeFlinch( m_LastHitGroup, info.GetAttacker() ) ) |
|
{ |
|
if ( GetAbsVelocity().Length() < 300 ) |
|
{ |
|
m_flVelocityModifier = 0.65; |
|
} |
|
} |
|
else |
|
{ |
|
m_flVelocityModifier = 0.5; |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
//============================================================================= |
|
// [menglish] Store whether or not the knife did this damage as knives do bullet damage, |
|
// so we need to specifically check the weapon here |
|
bool bKnifeDamage = false; |
|
CCSPlayer *pPlayer = ToCSPlayer( info.GetAttacker() ); |
|
|
|
if ( pPlayer ) |
|
{ |
|
|
|
// [paquin. forest] if this is blast damage, and we haven't opted out with a cvar, |
|
// we need to get the armor ratio out of the inflictor |
|
|
|
if ( (info.GetDamageType() & DMG_BLAST) && !sv_legacy_grenade_damage.GetBool() ) |
|
{ |
|
|
|
// [paquin] if we know this is a grenade, use it's armor ratio, otherwise |
|
// use the he grenade armor ratio |
|
|
|
CBaseCSGrenadeProjectile *pGrenade = dynamic_cast< CBaseCSGrenadeProjectile * >( pInflictor ); |
|
CCSWeaponInfo* pWeaponInfo; |
|
|
|
if ( pGrenade && pGrenade->m_pWeaponInfo ) |
|
{ |
|
pWeaponInfo = pGrenade->m_pWeaponInfo; |
|
} |
|
else |
|
{ |
|
pWeaponInfo = GetWeaponInfo( WEAPON_HEGRENADE ); |
|
} |
|
|
|
if ( pWeaponInfo ) |
|
{ |
|
flArmorRatio *= pWeaponInfo->m_flArmorRatio; |
|
} |
|
} |
|
else |
|
{ |
|
CWeaponCSBase *pWeapon = pPlayer->GetActiveCSWeapon(); |
|
|
|
if ( pWeapon ) |
|
{ |
|
flArmorRatio *= pWeapon->GetCSWpnData().m_flArmorRatio; |
|
//Knives do bullet damage, so we need to specifically check the weapon here |
|
bKnifeDamage = pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_KNIFE; |
|
|
|
if ( info.GetDamageType() & DMG_BULLET && !bKnifeDamage && pPlayer->GetTeam() != GetTeam() ) |
|
{ |
|
CCS_GameStats.Event_ShotHit( pPlayer, info ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
// keep track of amount of damage last sustained |
|
m_lastDamageAmount = flDamage; |
|
|
|
// Deal with Armour |
|
if ( ArmorValue() && !( info.GetDamageType() & (DMG_FALL | DMG_DROWN)) && IsArmored( m_LastHitGroup ) ) |
|
{ |
|
float fDamageToHealth = flDamage * flArmorRatio; |
|
float fDamageToArmor = (flDamage - fDamageToHealth) * flArmorBonus; |
|
|
|
int armorValue = ArmorValue(); |
|
|
|
// Does this use more armor than we have? |
|
if (fDamageToArmor > armorValue ) |
|
{ |
|
fDamageToHealth = flDamage - armorValue / flArmorBonus; |
|
fDamageToArmor = armorValue; |
|
armorValue = 0; |
|
} |
|
else |
|
{ |
|
if ( fDamageToArmor < 0 ) |
|
fDamageToArmor = 1; |
|
|
|
armorValue -= fDamageToArmor; |
|
} |
|
m_lastDamageArmor = (int)fDamageToArmor; |
|
SetArmorValue(armorValue); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj] Handle headshot-surviving achievement |
|
//============================================================================= |
|
if (m_LastHitGroup == HITGROUP_HEAD && flDamage > m_iHealth && fDamageToHealth < m_iHealth) |
|
{ |
|
m_bSurvivedHeadshotDueToHelmet = true; |
|
} |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
flDamage = fDamageToHealth; |
|
|
|
info.SetDamage( flDamage ); |
|
|
|
if ( ArmorValue() <= 0.0) |
|
m_bHasHelmet = false; |
|
|
|
if( !(info.GetDamageType() & DMG_FALL) ) |
|
Pain( true /*has armor*/ ); |
|
} |
|
else |
|
{ |
|
m_lastDamageArmor = 0; |
|
if( !(info.GetDamageType() & DMG_FALL) ) |
|
Pain( false /*no armor*/ ); |
|
} |
|
|
|
// round damage to integer |
|
m_lastDamageHealth = (int)flDamage; |
|
info.SetDamage( m_lastDamageHealth ); |
|
|
|
if ( info.GetDamage() <= 0 ) |
|
return 0; |
|
|
|
CSingleUserRecipientFilter user( this ); |
|
user.MakeReliable(); |
|
UserMessageBegin( user, "Damage" ); |
|
WRITE_BYTE( (int)info.GetDamage() ); |
|
WRITE_VEC3COORD( info.GetInflictor()->WorldSpaceCenter() ); |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Send the info about where the player was hit |
|
//============================================================================= |
|
if ( !( info.GetDamageType() & DMG_BULLET ) || bKnifeDamage ) |
|
{ |
|
WRITE_LONG( -1 ); |
|
} |
|
else |
|
{ |
|
WRITE_LONG( m_LastHitBox ); |
|
} |
|
WRITE_VEC3COORD( m_vLastHitLocationObjectSpace ); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
MessageEnd(); |
|
|
|
// Do special explosion damage effect |
|
if ( info.GetDamageType() & DMG_BLAST ) |
|
{ |
|
OnDamagedByExplosion( info ); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Achievement award for kill stealing i.e. killing an enemy who was very damaged from other players |
|
// [Forrest] Moved this check before RecordDamageTaken so that the damage currently being dealt by this player |
|
// won't disqualify them from getting the achievement. |
|
//============================================================================= |
|
|
|
if(m_iHealth - info.GetDamage() <= 0 && m_iHealth <= AchievementConsts::KillLowDamage_MaxHealthLeft) |
|
{ |
|
bool onlyDamage = true; |
|
CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker()); |
|
if(pAttacker && pAttacker->GetTeamNumber() != GetTeamNumber()) |
|
{ |
|
//Verify that the killer has not done damage to this player beforehand |
|
FOR_EACH_LL( m_DamageTakenList, i ) |
|
{ |
|
if( Q_strncmp( pAttacker->GetPlayerName(), m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 ) |
|
{ |
|
onlyDamage = false; |
|
break; |
|
} |
|
} |
|
if(onlyDamage) |
|
{ |
|
pAttacker->AwardAchievement(CSKillLowDamage); |
|
} |
|
} |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
#if REPORT_PLAYER_DAMAGE |
|
// damage output spew |
|
char dmgtype[64]; |
|
CTakeDamageInfo::DebugGetDamageTypeString( info.GetDamageType(), dmgtype, sizeof(dmgtype) ); |
|
|
|
if ( info.GetDamageType() & DMG_HEADSHOT ) |
|
Q_strncat(dmgtype, "HEADSHOT", sizeof(dmgtype)); |
|
|
|
char outputString[256]; |
|
Q_snprintf( outputString, sizeof(outputString), "%f: Player %s incoming %f damage from %s, type %s; applied %d health and %d armor\n", |
|
gpGlobals->curtime, GetPlayerName(), |
|
inputInfo.GetDamage(), info.GetInflictor()->GetDebugName(), dmgtype, |
|
m_lastDamageHealth, m_lastDamageArmor); |
|
|
|
Msg(outputString); |
|
#endif |
|
|
|
|
|
if ( pPlayer ) |
|
{ |
|
// Record for the shooter |
|
pPlayer->RecordDamageGiven( GetPlayerName(), info.GetDamage() ); |
|
|
|
// And for the victim |
|
RecordDamageTaken( pPlayer->GetPlayerName(), info.GetDamage() ); |
|
} |
|
else |
|
{ |
|
RecordDamageTaken( "world", info.GetDamage() ); |
|
} |
|
|
|
m_vecTotalBulletForce += info.GetDamageForce(); |
|
|
|
gamestats->Event_PlayerDamage( this, info ); |
|
|
|
return CBaseCombatCharacter::OnTakeDamage( info ); |
|
} |
|
else |
|
{ |
|
return 0; |
|
} |
|
} |
|
|
|
|
|
//MIKETODO: this probably should let the shield model catch the trace attacks. |
|
bool CCSPlayer::IsHittingShield( const Vector &vecDirection, trace_t *ptr ) |
|
{ |
|
if ( HasShield() == false ) |
|
return false; |
|
|
|
if ( IsShieldDrawn() == false ) |
|
return false; |
|
|
|
float flDot; |
|
Vector vForward; |
|
Vector2D vec2LOS = vecDirection.AsVector2D(); |
|
AngleVectors( GetLocalAngles(), &vForward ); |
|
|
|
Vector2DNormalize( vForward.AsVector2D() ); |
|
Vector2DNormalize( vec2LOS ); |
|
|
|
flDot = DotProduct2D ( vec2LOS , vForward.AsVector2D() ); |
|
|
|
if ( flDot < -0.87f ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
|
|
void CCSPlayer::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator ) |
|
{ |
|
bool bShouldBleed = true; |
|
bool bShouldSpark = false; |
|
bool bHitShield = IsHittingShield( vecDir, ptr ); |
|
|
|
CBasePlayer *pAttacker = (CBasePlayer*)ToBasePlayer( info.GetAttacker() ); |
|
|
|
// show blood for firendly fire only if FF is on |
|
if ( pAttacker && ( GetTeamNumber() == pAttacker->GetTeamNumber() ) ) |
|
bShouldBleed = CSGameRules()->IsFriendlyFireOn(); |
|
|
|
if ( m_takedamage != DAMAGE_YES ) |
|
return; |
|
|
|
m_LastHitGroup = ptr->hitgroup; |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Used when calculating the position this player was hit at in the bone space |
|
//============================================================================= |
|
m_LastHitBox = ptr->hitbox; |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
m_nForceBone = ptr->physicsbone; //Save this bone for ragdoll |
|
|
|
float flDamage = info.GetDamage(); |
|
|
|
bool bHeadShot = false; |
|
|
|
if ( bHitShield ) |
|
{ |
|
flDamage = 0; |
|
bShouldBleed = false; |
|
bShouldSpark = true; |
|
} |
|
else if( info.GetDamageType() & DMG_BLAST ) |
|
{ |
|
if ( ArmorValue() > 0 ) |
|
bShouldBleed = false; |
|
|
|
if ( bShouldBleed == true ) |
|
{ |
|
// punch view if we have no armor |
|
QAngle punchAngle = GetPunchAngle(); |
|
punchAngle.x = flDamage * -0.1; |
|
|
|
if ( punchAngle.x < -4 ) |
|
punchAngle.x = -4; |
|
|
|
SetPunchAngle( punchAngle ); |
|
} |
|
} |
|
else |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Calculate the position this player was hit at in the bone space |
|
//============================================================================= |
|
|
|
matrix3x4_t boneTransformToWorld, boneTransformToObject; |
|
GetBoneTransform(GetHitboxBone(ptr->hitbox), boneTransformToWorld); |
|
MatrixInvert(boneTransformToWorld, boneTransformToObject); |
|
VectorTransform(ptr->endpos, boneTransformToObject, m_vLastHitLocationObjectSpace); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
switch ( ptr->hitgroup ) |
|
{ |
|
case HITGROUP_GENERIC: |
|
break; |
|
|
|
case HITGROUP_HEAD: |
|
|
|
if ( m_bHasHelmet ) |
|
{ |
|
// bShouldBleed = false; |
|
bShouldSpark = true; |
|
} |
|
|
|
flDamage *= 4; |
|
|
|
if ( !m_bHasHelmet ) |
|
{ |
|
QAngle punchAngle = GetPunchAngle(); |
|
punchAngle.x = flDamage * -0.5; |
|
|
|
if ( punchAngle.x < -12 ) |
|
punchAngle.x = -12; |
|
|
|
punchAngle.z = flDamage * random->RandomFloat(-1,1); |
|
|
|
if ( punchAngle.z < -9 ) |
|
punchAngle.z = -9; |
|
|
|
else if ( punchAngle.z > 9 ) |
|
punchAngle.z = 9; |
|
|
|
SetPunchAngle( punchAngle ); |
|
} |
|
|
|
bHeadShot = true; |
|
|
|
break; |
|
|
|
case HITGROUP_CHEST: |
|
|
|
flDamage *= 1.0; |
|
|
|
if ( ArmorValue() <= 0 ) |
|
{ |
|
QAngle punchAngle = GetPunchAngle(); |
|
punchAngle.x = flDamage * -0.1; |
|
|
|
if ( punchAngle.x < -4 ) |
|
punchAngle.x = -4; |
|
|
|
SetPunchAngle( punchAngle ); |
|
} |
|
break; |
|
|
|
case HITGROUP_STOMACH: |
|
|
|
flDamage *= 1.25; |
|
|
|
if ( ArmorValue() <= 0 ) |
|
{ |
|
QAngle punchAngle = GetPunchAngle(); |
|
punchAngle.x = flDamage * -0.1; |
|
|
|
if ( punchAngle.x < -4 ) |
|
punchAngle.x = -4; |
|
|
|
SetPunchAngle( punchAngle ); |
|
} |
|
|
|
break; |
|
|
|
case HITGROUP_LEFTARM: |
|
case HITGROUP_RIGHTARM: |
|
flDamage *= 1.0; |
|
break; |
|
|
|
case HITGROUP_LEFTLEG: |
|
case HITGROUP_RIGHTLEG: |
|
flDamage *= 0.75; |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
|
|
// Since this code only runs on the server, make sure it shows the tempents it creates. |
|
CDisablePredictionFiltering disabler; |
|
|
|
if ( bShouldBleed ) |
|
{ |
|
// This does smaller splotches on the guy and splats blood on the world. |
|
TraceBleed( flDamage, vecDir, ptr, info.GetDamageType() ); |
|
|
|
CEffectData data; |
|
data.m_vOrigin = ptr->endpos; |
|
data.m_vNormal = vecDir * -1; |
|
data.m_nEntIndex = ptr->m_pEnt ? ptr->m_pEnt->entindex() : 0; |
|
data.m_flMagnitude = flDamage; |
|
|
|
// reduce blood effect if target has armor |
|
if ( ArmorValue() > 0 ) |
|
data.m_flMagnitude *= 0.5f; |
|
|
|
// reduce blood effect if target is hit in the helmet |
|
if ( ptr->hitgroup == HITGROUP_HEAD && bShouldSpark ) |
|
data.m_flMagnitude *= 0.5; |
|
|
|
DispatchEffect( "csblood", data ); |
|
} |
|
if ( ( ptr->hitgroup == HITGROUP_HEAD || bHitShield ) && bShouldSpark ) // they hit a helmet |
|
{ |
|
// show metal spark effect |
|
g_pEffects->Sparks( ptr->endpos, 1, 1, &ptr->plane.normal ); |
|
} |
|
|
|
if ( !bHitShield ) |
|
{ |
|
CTakeDamageInfo subInfo = info; |
|
|
|
subInfo.SetDamage( flDamage ); |
|
|
|
if( bHeadShot ) |
|
subInfo.AddDamageType( DMG_HEADSHOT ); |
|
|
|
AddMultiDamage( subInfo, this ); |
|
} |
|
} |
|
|
|
|
|
void CCSPlayer::Reset() |
|
{ |
|
ResetFragCount(); |
|
ResetDeathCount(); |
|
m_iAccount = 0; |
|
AddAccount( -16000, false ); |
|
|
|
//remove any weapons they bought before the round started |
|
RemoveAllItems( true ); |
|
|
|
//RemoveShield(); |
|
|
|
AddAccount( CSGameRules()->GetStartMoney(), true ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Displays a hint message to the player |
|
// Input : *pMessage - |
|
// bDisplayIfDead - |
|
// bOverrideClientSettings - |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::HintMessage( const char *pMessage, bool bDisplayIfDead, bool bOverrideClientSettings ) |
|
{ |
|
if ( ( !bDisplayIfDead && !IsAlive() ) || !IsNetClient() || !m_pHintMessageQueue ) |
|
return; |
|
|
|
if ( bOverrideClientSettings || m_bShowHints ) |
|
m_pHintMessageQueue->AddMessage( pMessage ); |
|
} |
|
|
|
void CCSPlayer::AddAccount( int amount, bool bTrackChange, bool bItemBought, const char *pItemName ) |
|
{ |
|
m_iAccount += amount; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Description of reason for change |
|
//============================================================================= |
|
|
|
if(amount > 0) |
|
{ |
|
CCS_GameStats.Event_MoneyEarned( this, amount ); |
|
} |
|
else if( amount < 0 && bItemBought) |
|
{ |
|
CCS_GameStats.Event_MoneySpent( this, ABS(amount), pItemName ); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
if ( m_iAccount < 0 ) |
|
m_iAccount = 0; |
|
else if ( m_iAccount > 16000 ) |
|
m_iAccount = 16000; |
|
} |
|
|
|
void CCSPlayer::MarkAsNotReceivingMoneyNextRound() |
|
{ |
|
m_receivesMoneyNextRound = false; |
|
} |
|
|
|
bool CCSPlayer::DoesPlayerGetRoundStartMoney() |
|
{ |
|
return m_receivesMoneyNextRound; |
|
} |
|
|
|
CCSPlayer* CCSPlayer::Instance( int iEnt ) |
|
{ |
|
return dynamic_cast< CCSPlayer* >( CBaseEntity::Instance( INDEXENT( iEnt ) ) ); |
|
} |
|
|
|
|
|
void CCSPlayer::DropC4() |
|
{ |
|
} |
|
|
|
|
|
bool CCSPlayer::HasDefuser() |
|
{ |
|
return m_bHasDefuser; |
|
} |
|
|
|
void CCSPlayer::RemoveDefuser() |
|
{ |
|
m_bHasDefuser = false; |
|
} |
|
|
|
void CCSPlayer::GiveDefuser(bool bPickedUp /* = false */) |
|
{ |
|
if ( !m_bHasDefuser ) |
|
{ |
|
IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" ); |
|
if( event ) |
|
{ |
|
event->SetInt( "userid", GetUserID() ); |
|
event->SetString( "item", "defuser" ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
} |
|
|
|
m_bHasDefuser = true; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Added for fun-fact support |
|
//============================================================================= |
|
|
|
m_bPickedUpDefuser = bPickedUp; |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
} |
|
|
|
// player blinded by a flashbang |
|
void CCSPlayer::Blind( float holdTime, float fadeTime, float startingAlpha ) |
|
{ |
|
// Don't flash a spectator. |
|
color32 clr = {255, 255, 255, 255}; |
|
|
|
clr.a = startingAlpha; |
|
|
|
// estimate when we can see again |
|
float oldBlindUntilTime = m_blindUntilTime; |
|
float oldBlindStartTime = m_blindStartTime; |
|
m_blindUntilTime = MAX( m_blindUntilTime, gpGlobals->curtime + holdTime + 0.5f * fadeTime ); |
|
m_blindStartTime = gpGlobals->curtime; |
|
|
|
// Spectators get a lessened flash. |
|
if ( (GetObserverMode() != OBS_MODE_NONE) && (GetObserverMode() != OBS_MODE_IN_EYE) ) |
|
{ |
|
if ( !mp_fadetoblack.GetBool() ) |
|
{ |
|
clr.a = 150; |
|
|
|
fadeTime = MIN(fadeTime, 0.5f); // make sure the spectator flashbang time is 1/2 second or less. |
|
holdTime = MIN(holdTime, fadeTime * 0.5f); // adjust the hold time to match the fade time. |
|
UTIL_ScreenFade( this, clr, fadeTime, holdTime, FFADE_IN ); |
|
} |
|
} |
|
else |
|
{ |
|
fadeTime /= 1.4; |
|
|
|
if ( gpGlobals->curtime > oldBlindUntilTime ) |
|
{ |
|
// The previous flashbang is wearing off, or completely gone |
|
m_flFlashDuration = fadeTime; |
|
m_flFlashMaxAlpha = startingAlpha; |
|
} |
|
else |
|
{ |
|
// The previous flashbang is still going strong - only extend the duration |
|
float remainingDuration = oldBlindStartTime + m_flFlashDuration - gpGlobals->curtime; |
|
|
|
m_flFlashDuration = MAX( remainingDuration, fadeTime ); |
|
m_flFlashMaxAlpha = MAX( m_flFlashMaxAlpha, startingAlpha ); |
|
} |
|
|
|
// allow bots to react |
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_blind" ); |
|
if ( event ) |
|
{ |
|
event->SetInt( "userid", GetUserID() ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
} |
|
} |
|
|
|
void CCSPlayer::Deafen( float flDistance ) |
|
{ |
|
// Spectators don't get deafened |
|
if ( (GetObserverMode() == OBS_MODE_NONE) || (GetObserverMode() == OBS_MODE_IN_EYE) ) |
|
{ |
|
// dsp presets are defined in hl2/scripts/dsp_presets.txt |
|
|
|
int effect; |
|
|
|
if( flDistance < 600 ) |
|
{ |
|
effect = 134; |
|
} |
|
else if( flDistance < 800 ) |
|
{ |
|
effect = 135; |
|
} |
|
else if( flDistance < 1000 ) |
|
{ |
|
effect = 136; |
|
} |
|
else |
|
{ |
|
// too far for us to get an effect |
|
return; |
|
} |
|
|
|
CSingleUserRecipientFilter user( this ); |
|
enginesound->SetPlayerDSP( user, effect, false ); |
|
|
|
//TODO: bots can't hear sound for a while? |
|
} |
|
} |
|
|
|
void CCSPlayer::GiveShield( void ) |
|
{ |
|
#ifdef CS_SHIELD_ENABLED |
|
m_bHasShield = true; |
|
m_bShieldDrawn = false; |
|
|
|
if ( HasSecondaryWeapon() ) |
|
{ |
|
CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
pWeapon->SetModel( pWeapon->GetViewModel() ); |
|
pWeapon->Deploy(); |
|
} |
|
|
|
CBaseViewModel *pVM = GetViewModel( 1 ); |
|
|
|
if ( pVM ) |
|
{ |
|
ShowViewModel( true ); |
|
pVM->RemoveEffects( EF_NODRAW ); |
|
pVM->SetWeaponModel( SHIELD_VIEW_MODEL, GetActiveWeapon() ); |
|
pVM->SendViewModelMatchingSequence( 1 ); |
|
} |
|
#endif |
|
} |
|
|
|
void CCSPlayer::RemoveShield( void ) |
|
{ |
|
#ifdef CS_SHIELD_ENABLED |
|
m_bHasShield = false; |
|
|
|
CBaseViewModel *pVM = GetViewModel( 1 ); |
|
|
|
if ( pVM ) |
|
{ |
|
pVM->AddEffects( EF_NODRAW ); |
|
} |
|
#endif |
|
} |
|
|
|
void CCSPlayer::RemoveAllItems( bool removeSuit ) |
|
{ |
|
if( HasDefuser() ) |
|
{ |
|
RemoveDefuser(); |
|
} |
|
|
|
if ( HasShield() ) |
|
{ |
|
RemoveShield(); |
|
} |
|
|
|
m_bHasNightVision = false; |
|
m_bNightVisionOn = false; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Added for fun-fact support |
|
//============================================================================= |
|
|
|
m_bPickedUpDefuser = false; |
|
m_bDefusedWithPickedUpKit = false; |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
if ( removeSuit ) |
|
{ |
|
m_bHasHelmet = false; |
|
SetArmorValue( 0 ); |
|
} |
|
|
|
BaseClass::RemoveAllItems( removeSuit ); |
|
} |
|
|
|
void CCSPlayer::ObserverRoundRespawn() |
|
{ |
|
ClearFlashbangScreenFade(); |
|
|
|
// did we change our name last round? |
|
if ( m_szNewName[0] != 0 ) |
|
{ |
|
// ... and force the name change now. After this happens, the gamerules will get |
|
// a ClientSettingsChanged callback from the above ClientCommand, but the name |
|
// matches what we're setting here, so it will do nothing. |
|
ChangeName( m_szNewName ); |
|
m_szNewName[0] = 0; |
|
} |
|
} |
|
|
|
void CCSPlayer::RoundRespawn() |
|
{ |
|
//MIKETODO: menus |
|
//if ( m_iMenu != Menu_ChooseAppearance ) |
|
{ |
|
// Put them back into the game. |
|
StopObserverMode(); |
|
State_Transition( STATE_ACTIVE ); |
|
respawn( this, false ); |
|
m_nButtons = 0; |
|
SetNextThink( TICK_NEVER_THINK ); |
|
} |
|
|
|
m_receivesMoneyNextRound = true; // reset this variable so they can receive their cash next round. |
|
|
|
//If they didn't die, this will print out their damage info |
|
OutputDamageGiven(); |
|
OutputDamageTaken(); |
|
ResetDamageCounters(); |
|
} |
|
|
|
void CCSPlayer::CheckTKPunishment( void ) |
|
{ |
|
// teamkill punishment.. |
|
if ( (m_bJustKilledTeammate == true) && mp_tkpunish.GetInt() ) |
|
{ |
|
m_bJustKilledTeammate = false; |
|
m_bPunishedForTK = true; |
|
CommitSuicide(); |
|
} |
|
} |
|
|
|
CWeaponCSBase* CCSPlayer::GetActiveCSWeapon() const |
|
{ |
|
return dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() ); |
|
} |
|
|
|
void CCSPlayer::PreThink() |
|
{ |
|
BaseClass::PreThink(); |
|
if ( m_bAutoReload ) |
|
{ |
|
m_bAutoReload = false; |
|
m_nButtons |= IN_RELOAD; |
|
} |
|
|
|
if ( m_afButtonLast != m_nButtons ) |
|
m_flLastMovement = gpGlobals->curtime; |
|
|
|
if ( g_fGameOver ) |
|
return; |
|
|
|
State_PreThink(); |
|
|
|
if ( m_pHintMessageQueue ) |
|
m_pHintMessageQueue->Update(); |
|
|
|
//Reset bullet force accumulator, only lasts one frame |
|
m_vecTotalBulletForce = vec3_origin; |
|
|
|
if ( mp_autokick.GetBool() && !IsBot() && !IsHLTV() && !IsAutoKickDisabled() ) |
|
{ |
|
if ( m_flLastMovement + CSGameRules()->GetRoundLength()*2 < gpGlobals->curtime ) |
|
{ |
|
UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_idle_kick", GetPlayerName() ); |
|
engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", GetUserID() ) ); |
|
m_flLastMovement = gpGlobals->curtime; |
|
} |
|
} |
|
#ifndef _XBOX |
|
// CS would like their players to continue to update their LastArea since it is displayed in the hud voice chat UI |
|
// But we won't do the population tracking while dead. |
|
CNavArea *area = TheNavMesh->GetNavArea( GetAbsOrigin(), 1000 ); |
|
if (area && area != m_lastNavArea) |
|
{ |
|
m_lastNavArea = area; |
|
if ( area->GetPlace() != UNDEFINED_PLACE ) |
|
{ |
|
const char *placeName = TheNavMesh->PlaceToName( area->GetPlace() ); |
|
if ( placeName && *placeName ) |
|
{ |
|
Q_strncpy( m_szLastPlaceName.GetForModify(), placeName, MAX_PLACE_NAME_LENGTH ); |
|
} |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
void CCSPlayer::MoveToNextIntroCamera() |
|
{ |
|
m_pIntroCamera = gEntList.FindEntityByClassname( m_pIntroCamera, "point_viewcontrol" ); |
|
|
|
// if m_pIntroCamera is NULL we just were at end of list, start searching from start again |
|
if(!m_pIntroCamera) |
|
m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "point_viewcontrol"); |
|
|
|
// find the target |
|
CBaseEntity *Target = NULL; |
|
|
|
if( m_pIntroCamera ) |
|
{ |
|
Target = gEntList.FindEntityByName( NULL, STRING(m_pIntroCamera->m_target) ); |
|
} |
|
|
|
// if we still couldn't find a camera, goto T spawn |
|
if(!m_pIntroCamera) |
|
m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "info_player_terrorist"); |
|
|
|
SetViewOffset( vec3_origin ); // no view offset |
|
UTIL_SetSize( this, vec3_origin, vec3_origin ); // no bbox |
|
|
|
if( !Target ) //if there are no cameras(or the camera has no target, find a spawn point and black out the screen |
|
{ |
|
if ( m_pIntroCamera.IsValid() ) |
|
SetAbsOrigin( m_pIntroCamera->GetAbsOrigin() + VEC_VIEW ); |
|
|
|
SetAbsAngles( QAngle( 0, 0, 0 ) ); |
|
|
|
m_pIntroCamera = NULL; // never update again |
|
return; |
|
} |
|
|
|
|
|
Vector vCamera = Target->GetAbsOrigin() - m_pIntroCamera->GetAbsOrigin(); |
|
Vector vIntroCamera = m_pIntroCamera->GetAbsOrigin(); |
|
|
|
VectorNormalize( vCamera ); |
|
|
|
QAngle CamAngles; |
|
VectorAngles( vCamera, CamAngles ); |
|
|
|
SetAbsOrigin( vIntroCamera ); |
|
SetAbsAngles( CamAngles ); |
|
SnapEyeAngles( CamAngles ); |
|
m_fIntroCamTime = gpGlobals->curtime + 6; |
|
} |
|
|
|
class NotVIP |
|
{ |
|
public: |
|
bool operator()( CBasePlayer *player ) |
|
{ |
|
CCSPlayer *csPlayer = static_cast< CCSPlayer * >(player); |
|
csPlayer->MakeVIP( false ); |
|
|
|
return true; |
|
} |
|
}; |
|
|
|
// Expose the VIP selection to plugins, since we don't have an official VIP mode. This |
|
// allows plugins to access the (limited) VIP functionality already present (scoreboard |
|
// identification and radar color). |
|
CON_COMMAND( cs_make_vip, "Marks a player as the VIP" ) |
|
{ |
|
if ( !UTIL_IsCommandIssuedByServerAdmin() ) |
|
return; |
|
|
|
if ( args.ArgC() != 2 ) |
|
{ |
|
return; |
|
} |
|
|
|
CCSPlayer *player = static_cast< CCSPlayer * >(UTIL_PlayerByIndex( atoi( args[1] ) )); |
|
if ( !player ) |
|
{ |
|
// Invalid value clears out VIP |
|
NotVIP notVIP; |
|
ForEachPlayer( notVIP ); |
|
return; |
|
} |
|
|
|
player->MakeVIP( true ); |
|
} |
|
|
|
void CCSPlayer::MakeVIP( bool isVIP ) |
|
{ |
|
if ( isVIP ) |
|
{ |
|
NotVIP notVIP; |
|
ForEachPlayer( notVIP ); |
|
} |
|
m_isVIP = isVIP; |
|
} |
|
|
|
bool CCSPlayer::IsVIP() const |
|
{ |
|
return m_isVIP; |
|
} |
|
|
|
void CCSPlayer::DropShield( void ) |
|
{ |
|
#ifdef CS_SHIELD_ENABLED |
|
//Drop an item_defuser |
|
Vector vForward, vRight; |
|
AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL ); |
|
|
|
RemoveShield(); |
|
|
|
CBaseAnimating *pShield = (CBaseAnimating *)CBaseEntity::Create( "item_shield", WorldSpaceCenter(), GetLocalAngles() ); |
|
pShield->ApplyAbsVelocityImpulse( vForward * 200 + vRight * random->RandomFloat( -50, 50 ) ); |
|
|
|
CBaseCombatWeapon *pActive = GetActiveWeapon(); |
|
|
|
if ( pActive ) |
|
{ |
|
pActive->Deploy(); |
|
} |
|
#endif |
|
} |
|
|
|
void CCSPlayer::SetShieldDrawnState( bool bState ) |
|
{ |
|
#ifdef CS_SHIELD_ENABLED |
|
m_bShieldDrawn = bState; |
|
#endif |
|
} |
|
|
|
bool CCSPlayer::CSWeaponDrop( CBaseCombatWeapon *pWeapon, bool bDropShield, bool bThrowForward ) |
|
{ |
|
bool bSuccess = false; |
|
|
|
if ( HasShield() && bDropShield == true ) |
|
{ |
|
DropShield(); |
|
return true; |
|
} |
|
|
|
if ( pWeapon ) |
|
{ |
|
Vector vForward; |
|
|
|
AngleVectors( EyeAngles(), &vForward, NULL, NULL ); |
|
//GetVectors( &vForward, NULL, NULL ); |
|
Vector vTossPos = WorldSpaceCenter(); |
|
|
|
if( bThrowForward ) |
|
vTossPos = vTossPos + vForward * 64; |
|
|
|
Weapon_Drop( pWeapon, &vTossPos, NULL ); |
|
|
|
pWeapon->SetSolidFlags( FSOLID_NOT_STANDABLE | FSOLID_TRIGGER | FSOLID_USE_TRIGGER_BOUNDS ); |
|
pWeapon->SetMoveCollide( MOVECOLLIDE_FLY_BOUNCE ); |
|
|
|
CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon ); |
|
|
|
if( pCSWeapon ) |
|
{ |
|
pCSWeapon->SetWeaponModelIndex( pCSWeapon->GetCSWpnData().szWorldModel ); |
|
|
|
//Find out the index of the ammo type |
|
int iAmmoIndex = pCSWeapon->GetPrimaryAmmoType(); |
|
|
|
//If it has an ammo type, find out how much the player has |
|
if( iAmmoIndex != -1 ) |
|
{ |
|
// Check to make sure we don't have other weapons using this ammo type |
|
bool bAmmoTypeInUse = false; |
|
if ( IsAlive() && GetHealth() > 0 ) |
|
{ |
|
for ( int i=0; i<MAX_WEAPONS; ++i ) |
|
{ |
|
CBaseCombatWeapon *pOtherWeapon = GetWeapon(i); |
|
if ( pOtherWeapon && pOtherWeapon != pWeapon && pOtherWeapon->GetPrimaryAmmoType() == iAmmoIndex ) |
|
{ |
|
bAmmoTypeInUse = true; |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if ( !bAmmoTypeInUse ) |
|
{ |
|
int iAmmoToDrop = GetAmmoCount( iAmmoIndex ); |
|
|
|
//Add this much to the dropped weapon |
|
pCSWeapon->SetExtraAmmoCount( iAmmoToDrop ); |
|
|
|
//Remove all ammo of this type from the player |
|
SetAmmoCount( 0, iAmmoIndex ); |
|
} |
|
} |
|
} |
|
|
|
//========================================= |
|
// Teleport the weapon to the player's hand |
|
//========================================= |
|
int iBIndex = -1; |
|
int iWeaponBoneIndex = -1; |
|
|
|
MDLCACHE_CRITICAL_SECTION(); |
|
CStudioHdr *hdr = pWeapon->GetModelPtr(); |
|
// If I have a hand, set the weapon position to my hand bone position. |
|
if ( hdr && hdr->numbones() > 0 ) |
|
{ |
|
// Assume bone zero is the root |
|
for ( iWeaponBoneIndex = 0; iWeaponBoneIndex < hdr->numbones(); ++iWeaponBoneIndex ) |
|
{ |
|
iBIndex = LookupBone( hdr->pBone( iWeaponBoneIndex )->pszName() ); |
|
// Found one! |
|
if ( iBIndex != -1 ) |
|
{ |
|
break; |
|
} |
|
} |
|
|
|
if ( iWeaponBoneIndex == hdr->numbones() ) |
|
return true; |
|
|
|
if ( iBIndex == -1 ) |
|
{ |
|
iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" ); |
|
} |
|
} |
|
else |
|
{ |
|
iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" ); |
|
} |
|
|
|
if ( iBIndex != -1) |
|
{ |
|
Vector origin; |
|
QAngle angles; |
|
matrix3x4_t transform; |
|
|
|
// Get the transform for the weapon bonetoworldspace in the NPC |
|
GetBoneTransform( iBIndex, transform ); |
|
|
|
// find offset of root bone from origin in local space |
|
// Make sure we're detached from hierarchy before doing this!!! |
|
pWeapon->StopFollowingEntity(); |
|
pWeapon->SetAbsOrigin( Vector( 0, 0, 0 ) ); |
|
pWeapon->SetAbsAngles( QAngle( 0, 0, 0 ) ); |
|
pWeapon->InvalidateBoneCache(); |
|
matrix3x4_t rootLocal; |
|
pWeapon->GetBoneTransform( iWeaponBoneIndex, rootLocal ); |
|
|
|
// invert it |
|
matrix3x4_t rootInvLocal; |
|
MatrixInvert( rootLocal, rootInvLocal ); |
|
|
|
matrix3x4_t weaponMatrix; |
|
ConcatTransforms( transform, rootInvLocal, weaponMatrix ); |
|
MatrixAngles( weaponMatrix, angles, origin ); |
|
|
|
pWeapon->Teleport( &origin, &angles, NULL ); |
|
|
|
//Have to teleport the physics object as well |
|
|
|
IPhysicsObject *pWeaponPhys = pWeapon->VPhysicsGetObject(); |
|
|
|
if( pWeaponPhys ) |
|
{ |
|
Vector vPos; |
|
QAngle vAngles; |
|
pWeaponPhys->GetPosition( &vPos, &vAngles ); |
|
pWeaponPhys->SetPosition( vPos, angles, true ); |
|
|
|
AngularImpulse angImp(0,0,0); |
|
Vector vecAdd = GetAbsVelocity(); |
|
pWeaponPhys->AddVelocity( &vecAdd, &angImp ); |
|
} |
|
} |
|
|
|
bSuccess = true; |
|
} |
|
|
|
return bSuccess; |
|
} |
|
|
|
|
|
bool CCSPlayer::DropRifle( bool fromDeath ) |
|
{ |
|
bool bSuccess = false; |
|
|
|
CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE ); |
|
if ( pWeapon ) |
|
{ |
|
bSuccess = CSWeaponDrop( pWeapon, false ); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Add the dropped weapon to the dropped equipment list |
|
//============================================================================= |
|
if( fromDeath && bSuccess ) |
|
{ |
|
m_hDroppedEquipment[DROPPED_WEAPON] = static_cast<CBaseEntity *>(pWeapon); |
|
} |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
return bSuccess; |
|
} |
|
|
|
|
|
bool CCSPlayer::DropPistol( bool fromDeath ) |
|
{ |
|
bool bSuccess = false; |
|
|
|
CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
if ( pWeapon ) |
|
{ |
|
bSuccess = CSWeaponDrop( pWeapon, false ); |
|
m_bUsingDefaultPistol = false; |
|
} |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Add the dropped weapon to the dropped equipment list |
|
//============================================================================= |
|
if( fromDeath && bSuccess ) |
|
{ |
|
m_hDroppedEquipment[DROPPED_WEAPON] = static_cast<CBaseEntity *>(pWeapon); |
|
} |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
return bSuccess; |
|
} |
|
|
|
bool CCSPlayer::HasPrimaryWeapon( void ) |
|
{ |
|
bool bSuccess = false; |
|
|
|
CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_RIFLE ); |
|
|
|
if ( pWeapon ) |
|
{ |
|
bSuccess = true; |
|
} |
|
|
|
return bSuccess; |
|
} |
|
|
|
|
|
bool CCSPlayer::HasSecondaryWeapon( void ) |
|
{ |
|
bool bSuccess = false; |
|
|
|
CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
if ( pWeapon ) |
|
{ |
|
bSuccess = true; |
|
} |
|
|
|
return bSuccess; |
|
} |
|
|
|
bool CCSPlayer::IsInBuyZone() |
|
{ |
|
return m_bInBuyZone && !IsVIP(); |
|
} |
|
|
|
bool CCSPlayer::CanPlayerBuy( bool display ) |
|
{ |
|
// is the player in a buy zone? |
|
if ( !IsInBuyZone() ) |
|
{ |
|
return false; |
|
} |
|
|
|
CCSGameRules* mp = CSGameRules(); |
|
|
|
// is the player alive? |
|
if ( m_lifeState != LIFE_ALIVE ) |
|
{ |
|
return false; |
|
} |
|
|
|
int buyTime = (int)(mp_buytime.GetFloat() * 60); |
|
|
|
if ( mp->IsBuyTimeElapsed() ) |
|
{ |
|
if ( display == true ) |
|
{ |
|
char strBuyTime[16]; |
|
Q_snprintf( strBuyTime, sizeof( strBuyTime ), "%d", buyTime ); |
|
ClientPrint( this, HUD_PRINTCENTER, "#Cant_buy", strBuyTime ); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
if ( m_bIsVIP ) |
|
{ |
|
if ( display == true ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#VIP_cant_buy" ); |
|
|
|
return false; |
|
} |
|
|
|
if ( mp->m_bCTCantBuy && ( GetTeamNumber() == TEAM_CT ) ) |
|
{ |
|
if ( display == true ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#CT_cant_buy" ); |
|
|
|
return false; |
|
} |
|
|
|
if ( mp->m_bTCantBuy && ( GetTeamNumber() == TEAM_TERRORIST ) ) |
|
{ |
|
if ( display == true ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Terrorist_cant_buy" ); |
|
|
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
BuyResult_e CCSPlayer::AttemptToBuyVest( void ) |
|
{ |
|
int iKevlarPrice = KEVLAR_PRICE; |
|
|
|
if ( CSGameRules()->IsBlackMarket() ) |
|
{ |
|
iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR ); |
|
} |
|
|
|
if ( ArmorValue() >= 100 ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar" ); |
|
return BUY_ALREADY_HAVE; |
|
} |
|
else if ( m_iAccount < iKevlarPrice ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" ); |
|
return BUY_CANT_AFFORD; |
|
} |
|
else |
|
{ |
|
if ( m_bHasHelmet ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" ); |
|
} |
|
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" ); |
|
if( event ) |
|
{ |
|
event->SetInt( "userid", GetUserID() ); |
|
event->SetString( "item", "vest" ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
|
|
GiveNamedItem( "item_kevlar" ); |
|
AddAccount( -iKevlarPrice, true, true, "item_kevlar" ); |
|
BlackMarketAddWeapon( "item_kevlar", this ); |
|
return BUY_BOUGHT; |
|
} |
|
} |
|
|
|
|
|
BuyResult_e CCSPlayer::AttemptToBuyAssaultSuit( void ) |
|
{ |
|
// WARNING: This price logic also exists in C_CSPlayer::GetCurrentAssaultSuitPrice |
|
// and must be kept in sync if changes are made. |
|
|
|
int fullArmor = ArmorValue() >= 100 ? 1 : 0; |
|
|
|
int price = 0, enoughMoney = 0; |
|
|
|
int iHelmetPrice = HELMET_PRICE; |
|
int iKevlarPrice = KEVLAR_PRICE; |
|
int iAssaultSuitPrice = ASSAULTSUIT_PRICE; |
|
|
|
if ( CSGameRules()->IsBlackMarket() ) |
|
{ |
|
iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR ); |
|
iAssaultSuitPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_ASSAULTSUIT ); |
|
|
|
iHelmetPrice = iAssaultSuitPrice - iKevlarPrice; |
|
} |
|
|
|
if ( fullArmor && m_bHasHelmet ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Helmet" ); |
|
return BUY_ALREADY_HAVE; |
|
} |
|
else if ( fullArmor && !m_bHasHelmet && m_iAccount >= iHelmetPrice ) |
|
{ |
|
enoughMoney = 1; |
|
price = iHelmetPrice; |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Kevlar_Bought_Helmet" ); |
|
} |
|
else if ( !fullArmor && m_bHasHelmet && m_iAccount >= iKevlarPrice ) |
|
{ |
|
enoughMoney = 1; |
|
price = iKevlarPrice; |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_Helmet_Bought_Kevlar" ); |
|
} |
|
else if ( m_iAccount >= iAssaultSuitPrice ) |
|
{ |
|
enoughMoney = 1; |
|
price = iAssaultSuitPrice; |
|
} |
|
|
|
// process the result |
|
if ( !enoughMoney ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" ); |
|
return BUY_CANT_AFFORD; |
|
} |
|
else |
|
{ |
|
IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" ); |
|
if( event ) |
|
{ |
|
event->SetInt( "userid", GetUserID() ); |
|
event->SetString( "item", "vesthelm" ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
|
|
GiveNamedItem( "item_assaultsuit" ); |
|
AddAccount( -price, true, true, "item_assaultsuit" ); |
|
BlackMarketAddWeapon( "item_assaultsuit", this ); |
|
return BUY_BOUGHT; |
|
} |
|
} |
|
|
|
BuyResult_e CCSPlayer::AttemptToBuyShield( void ) |
|
{ |
|
#ifdef CS_SHIELD_ENABLED |
|
if ( HasShield() ) // prevent this guy from buying more than 1 Defuse Kit |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" ); |
|
return BUY_ALREADY_HAVE; |
|
} |
|
else if ( m_iAccount < SHIELD_PRICE ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" ); |
|
return BUY_CANT_AFFORD; |
|
} |
|
else |
|
{ |
|
if ( HasSecondaryWeapon() ) |
|
{ |
|
CBaseCombatWeapon *pWeapon = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon ); |
|
|
|
if ( pCSWeapon && pCSWeapon->GetCSWpnData().m_bCanUseWithShield == false ) |
|
return; |
|
} |
|
|
|
if ( HasPrimaryWeapon() ) |
|
DropRifle(); |
|
|
|
GiveShield(); |
|
|
|
CPASAttenuationFilter filter( this, "Player.PickupWeapon" ); |
|
EmitSound( filter, entindex(), "Player.PickupWeapon" ); |
|
|
|
m_bAnythingBought = true; |
|
AddAccount( -SHIELD_PRICE, true, true, "item_shield" ); |
|
return BUY_BOUGHT; |
|
} |
|
#else |
|
ClientPrint( this, HUD_PRINTCENTER, "Tactical shield disabled" ); |
|
return BUY_NOT_ALLOWED; |
|
#endif |
|
} |
|
|
|
BuyResult_e CCSPlayer::AttemptToBuyDefuser( void ) |
|
{ |
|
CCSGameRules *MPRules = CSGameRules(); |
|
|
|
if( ( GetTeamNumber() == TEAM_CT ) && MPRules->IsBombDefuseMap() ) |
|
{ |
|
if ( HasDefuser() ) // prevent this guy from buying more than 1 Defuse Kit |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" ); |
|
return BUY_ALREADY_HAVE; |
|
} |
|
else if ( m_iAccount < DEFUSEKIT_PRICE ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" ); |
|
return BUY_CANT_AFFORD; |
|
} |
|
else |
|
{ |
|
GiveDefuser(); |
|
|
|
CPASAttenuationFilter filter( this, "Player.PickupWeapon" ); |
|
EmitSound( filter, entindex(), "Player.PickupWeapon" ); |
|
|
|
AddAccount( -DEFUSEKIT_PRICE, true, true, "item_defuser" ); |
|
return BUY_BOUGHT; |
|
} |
|
} |
|
|
|
return BUY_NOT_ALLOWED; |
|
} |
|
|
|
BuyResult_e CCSPlayer::AttemptToBuyNightVision( void ) |
|
{ |
|
int iNVGPrice = NVG_PRICE; |
|
|
|
if ( CSGameRules()->IsBlackMarket() ) |
|
{ |
|
iNVGPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_NVG ); |
|
} |
|
|
|
if ( m_bHasNightVision == TRUE ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Already_Have_One" ); |
|
return BUY_ALREADY_HAVE; |
|
} |
|
else if ( m_iAccount < iNVGPrice ) |
|
{ |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" ); |
|
return BUY_CANT_AFFORD; |
|
} |
|
else |
|
{ |
|
IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" ); |
|
if( event ) |
|
{ |
|
event->SetInt( "userid", GetUserID() ); |
|
event->SetString( "item", "nvgs" ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
|
|
GiveNamedItem( "item_nvgs" ); |
|
AddAccount( -iNVGPrice, true, true ); |
|
BlackMarketAddWeapon( "nightvision", this ); |
|
|
|
if ( !(m_iDisplayHistoryBits & DHF_NIGHTVISION) ) |
|
{ |
|
HintMessage( "#Hint_use_nightvision", false ); |
|
m_iDisplayHistoryBits |= DHF_NIGHTVISION; |
|
} |
|
return BUY_BOUGHT; |
|
} |
|
} |
|
|
|
|
|
// Handles the special "buy" alias commands we're creating to accommodate the buy |
|
// scripts players use (now that we've rearranged the buy menus and broken the scripts) |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
//[tj] This is essentially a shim so I can easily check the return |
|
// value without adding new code to all the return points. |
|
//============================================================================= |
|
|
|
BuyResult_e CCSPlayer::HandleCommand_Buy( const char *item ) |
|
{ |
|
BuyResult_e result = HandleCommand_Buy_Internal(item); |
|
if (result == BUY_BOUGHT) |
|
{ |
|
m_bMadePurchseThisRound = true; |
|
CCS_GameStats.IncrementStat(this, CSSTAT_ITEMS_PURCHASED, 1); |
|
} |
|
return result; |
|
} |
|
|
|
BuyResult_e CCSPlayer::HandleCommand_Buy_Internal( const char* wpnName ) |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
{ |
|
BuyResult_e result = CanPlayerBuy( false ) ? BUY_PLAYER_CANT_BUY : BUY_INVALID_ITEM; // set some defaults |
|
|
|
// translate the new weapon names to the old ones that are actually being used. |
|
wpnName = GetTranslatedWeaponAlias(wpnName); |
|
|
|
CCSWeaponInfo *pWeaponInfo = GetWeaponInfo( AliasToWeaponID( wpnName ) ); |
|
if ( pWeaponInfo == NULL ) |
|
{ |
|
if ( Q_stricmp( wpnName, "primammo" ) == 0 ) |
|
{ |
|
result = AttemptToBuyAmmo( 0 ); |
|
} |
|
else if ( Q_stricmp( wpnName, "secammo" ) == 0 ) |
|
{ |
|
result = AttemptToBuyAmmo( 1 ); |
|
} |
|
else if ( Q_stristr( wpnName, "defuser" ) ) |
|
{ |
|
if( CanPlayerBuy( true ) ) |
|
{ |
|
result = AttemptToBuyDefuser(); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
|
|
if( !CanPlayerBuy( true ) ) |
|
{ |
|
return BUY_PLAYER_CANT_BUY; |
|
} |
|
|
|
BuyResult_e equipResult = BUY_INVALID_ITEM; |
|
|
|
if ( Q_stristr( wpnName, "kevlar" ) ) |
|
{ |
|
equipResult = AttemptToBuyVest(); |
|
} |
|
else if ( Q_stristr( wpnName, "assaultsuit" ) ) |
|
{ |
|
equipResult = AttemptToBuyAssaultSuit(); |
|
} |
|
else if ( Q_stristr( wpnName, "shield" ) ) |
|
{ |
|
equipResult = AttemptToBuyShield(); |
|
} |
|
else if ( Q_stristr( wpnName, "nightvision" ) ) |
|
{ |
|
equipResult = AttemptToBuyNightVision(); |
|
} |
|
|
|
if ( equipResult != BUY_INVALID_ITEM ) |
|
{ |
|
if ( equipResult == BUY_BOUGHT ) |
|
{ |
|
BuildRebuyStruct(); |
|
} |
|
return equipResult; // intentional early return here |
|
} |
|
|
|
bool bPurchase = false; |
|
|
|
// MIKETODO: assasination maps have a specific set of weapons that can be used in them. |
|
if ( pWeaponInfo->m_iTeam != TEAM_UNASSIGNED && GetTeamNumber() != pWeaponInfo->m_iTeam ) |
|
{ |
|
result = BUY_NOT_ALLOWED; |
|
if ( pWeaponInfo->m_WrongTeamMsg[0] != 0 ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#Alias_Not_Avail", pWeaponInfo->m_WrongTeamMsg ); |
|
} |
|
} |
|
else if ( pWeaponInfo->GetWeaponPrice() <= 0 ) |
|
{ |
|
// ClientPrint( this, HUD_PRINTCENTER, "#Cant_buy_this_item", pWeaponInfo->m_WrongTeamMsg ); |
|
} |
|
else if( pWeaponInfo->m_WeaponType == WEAPONTYPE_GRENADE ) |
|
{ |
|
// make sure the player can afford this item. |
|
if ( m_iAccount >= pWeaponInfo->GetWeaponPrice() ) |
|
{ |
|
bPurchase = true; |
|
|
|
const char *szWeaponName = NULL; |
|
int ammoMax = 0; |
|
if ( Q_strstr( pWeaponInfo->szClassName, "flashbang" ) ) |
|
{ |
|
szWeaponName = "weapon_flashbang"; |
|
ammoMax = ammo_flashbang_max.GetInt(); |
|
} |
|
else if ( Q_strstr( pWeaponInfo->szClassName, "hegrenade" ) ) |
|
{ |
|
szWeaponName = "weapon_hegrenade"; |
|
ammoMax = ammo_hegrenade_max.GetInt(); |
|
} |
|
else if ( Q_strstr( pWeaponInfo->szClassName, "smokegrenade" ) ) |
|
{ |
|
szWeaponName = "weapon_smokegrenade"; |
|
ammoMax = ammo_smokegrenade_max.GetInt(); |
|
} |
|
|
|
if ( szWeaponName != NULL ) |
|
{ |
|
CBaseCombatWeapon* pGrenadeWeapon = Weapon_OwnsThisType( szWeaponName ); |
|
{ |
|
if ( pGrenadeWeapon != NULL ) |
|
{ |
|
int nAmmoType = pGrenadeWeapon->GetPrimaryAmmoType(); |
|
|
|
if( nAmmoType != -1 ) |
|
{ |
|
if( GetAmmoCount(nAmmoType) >= ammoMax ) |
|
{ |
|
result = BUY_ALREADY_HAVE; |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Carry_Anymore" ); |
|
bPurchase = false; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
else if ( !Weapon_OwnsThisType( pWeaponInfo->szClassName ) ) //don't buy duplicate weapons |
|
{ |
|
// do they have enough money? |
|
if ( m_iAccount >= pWeaponInfo->GetWeaponPrice() ) |
|
{ |
|
if ( m_lifeState != LIFE_DEAD ) |
|
{ |
|
if ( pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL ) |
|
{ |
|
DropPistol(); |
|
} |
|
else if ( pWeaponInfo->iSlot == WEAPON_SLOT_RIFLE ) |
|
{ |
|
DropRifle(); |
|
} |
|
} |
|
|
|
bPurchase = true; |
|
} |
|
else |
|
{ |
|
result = BUY_CANT_AFFORD; |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" ); |
|
} |
|
} |
|
else |
|
{ |
|
result = BUY_ALREADY_HAVE; |
|
} |
|
|
|
if ( HasShield() ) |
|
{ |
|
if ( pWeaponInfo->m_bCanUseWithShield == false ) |
|
{ |
|
result = BUY_NOT_ALLOWED; |
|
bPurchase = false; |
|
} |
|
} |
|
|
|
if( bPurchase ) |
|
{ |
|
result = BUY_BOUGHT; |
|
|
|
if ( bPurchase && pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL ) |
|
m_bUsingDefaultPistol = false; |
|
|
|
GiveNamedItem( pWeaponInfo->szClassName ); |
|
AddAccount( -pWeaponInfo->GetWeaponPrice(), true, true, pWeaponInfo->szClassName ); |
|
BlackMarketAddWeapon( wpnName, this ); |
|
} |
|
} |
|
|
|
if ( result == BUY_BOUGHT ) |
|
{ |
|
BuildRebuyStruct(); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
BuyResult_e CCSPlayer::BuyGunAmmo( CBaseCombatWeapon *pWeapon, bool bBlinkMoney ) |
|
{ |
|
if ( !CanPlayerBuy( false ) ) |
|
{ |
|
return BUY_PLAYER_CANT_BUY; |
|
} |
|
|
|
// Ensure that the weapon uses ammo |
|
int nAmmo = pWeapon->GetPrimaryAmmoType(); |
|
if ( nAmmo == -1 ) |
|
{ |
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
// Can only buy if the player does not already have full ammo |
|
if ( GetAmmoCount( nAmmo ) >= GetAmmoDef()->MaxCarry( nAmmo ) ) |
|
{ |
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
// Purchase the ammo if the player has enough money |
|
if ( m_iAccount >= GetCSAmmoDef()->GetCost( nAmmo ) ) |
|
{ |
|
GiveAmmo( GetCSAmmoDef()->GetBuySize( nAmmo ), nAmmo, true ); |
|
AddAccount( -GetCSAmmoDef()->GetCost( nAmmo ), true, true, GetCSAmmoDef()->GetAmmoOfIndex( nAmmo )->pName ); |
|
return BUY_BOUGHT; |
|
} |
|
|
|
if ( bBlinkMoney ) |
|
{ |
|
// Not enough money.. let the player know |
|
if( !m_bIsInAutoBuy && !m_bIsInRebuy ) |
|
ClientPrint( this, HUD_PRINTCENTER, "#Not_Enough_Money" ); |
|
} |
|
|
|
return BUY_CANT_AFFORD; |
|
} |
|
|
|
|
|
BuyResult_e CCSPlayer::BuyAmmo( int nSlot, bool bBlinkMoney ) |
|
{ |
|
if ( !CanPlayerBuy( false ) ) |
|
{ |
|
return BUY_PLAYER_CANT_BUY; |
|
} |
|
|
|
if ( nSlot < 0 || nSlot > 1 ) |
|
{ |
|
return BUY_INVALID_ITEM; |
|
} |
|
|
|
// Buy one ammo clip for all weapons in the given slot |
|
// |
|
// nSlot == 1 : Primary weapons |
|
// nSlot == 2 : Secondary weapons |
|
|
|
CBaseCombatWeapon *pSlot = Weapon_GetSlot( nSlot ); |
|
if ( !pSlot ) |
|
return BUY_INVALID_ITEM; |
|
|
|
//MIKETODO: shield. |
|
//if ( player->HasShield() && player->m_rgpPlayerItems[2] ) |
|
// pItem = player->m_rgpPlayerItems[2]; |
|
|
|
return BuyGunAmmo( pSlot, bBlinkMoney ); |
|
} |
|
|
|
|
|
BuyResult_e CCSPlayer::AttemptToBuyAmmo( int iAmmoType ) |
|
{ |
|
Assert( iAmmoType == 0 || iAmmoType == 1 ); |
|
|
|
BuyResult_e result = BuyAmmo( iAmmoType, true ); |
|
|
|
if ( result == BUY_BOUGHT ) |
|
{ |
|
while ( BuyAmmo( iAmmoType, false ) == BUY_BOUGHT ) |
|
{ |
|
// empty loop - keep buying |
|
} |
|
|
|
return BUY_BOUGHT; |
|
} |
|
|
|
return result; |
|
} |
|
|
|
BuyResult_e CCSPlayer::AttemptToBuyAmmoSingle( int iAmmoType ) |
|
{ |
|
Assert( iAmmoType == 0 || iAmmoType == 1 ); |
|
|
|
BuyResult_e result = BuyAmmo( iAmmoType, true ); |
|
|
|
if ( result == BUY_BOUGHT ) |
|
{ |
|
BuildRebuyStruct(); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
const char *RadioEventName[ RADIO_NUM_EVENTS+1 ] = |
|
{ |
|
"RADIO_INVALID", |
|
|
|
"EVENT_START_RADIO_1", |
|
|
|
"EVENT_RADIO_COVER_ME", |
|
"EVENT_RADIO_YOU_TAKE_THE_POINT", |
|
"EVENT_RADIO_HOLD_THIS_POSITION", |
|
"EVENT_RADIO_REGROUP_TEAM", |
|
"EVENT_RADIO_FOLLOW_ME", |
|
"EVENT_RADIO_TAKING_FIRE", |
|
|
|
"EVENT_START_RADIO_2", |
|
|
|
"EVENT_RADIO_GO_GO_GO", |
|
"EVENT_RADIO_TEAM_FALL_BACK", |
|
"EVENT_RADIO_STICK_TOGETHER_TEAM", |
|
"EVENT_RADIO_GET_IN_POSITION_AND_WAIT", |
|
"EVENT_RADIO_STORM_THE_FRONT", |
|
"EVENT_RADIO_REPORT_IN_TEAM", |
|
|
|
"EVENT_START_RADIO_3", |
|
|
|
"EVENT_RADIO_AFFIRMATIVE", |
|
"EVENT_RADIO_ENEMY_SPOTTED", |
|
"EVENT_RADIO_NEED_BACKUP", |
|
"EVENT_RADIO_SECTOR_CLEAR", |
|
"EVENT_RADIO_IN_POSITION", |
|
"EVENT_RADIO_REPORTING_IN", |
|
"EVENT_RADIO_GET_OUT_OF_THERE", |
|
"EVENT_RADIO_NEGATIVE", |
|
"EVENT_RADIO_ENEMY_DOWN", |
|
|
|
"EVENT_RADIO_END", |
|
|
|
NULL // must be NULL-terminated |
|
}; |
|
|
|
|
|
/** |
|
* Convert name to RadioType |
|
*/ |
|
RadioType NameToRadioEvent( const char *name ) |
|
{ |
|
for( int i=0; RadioEventName[i]; ++i ) |
|
if (!stricmp( RadioEventName[i], name )) |
|
return static_cast<RadioType>( i ); |
|
|
|
return RADIO_INVALID; |
|
} |
|
|
|
|
|
void CCSPlayer::HandleMenu_Radio1( int slot ) |
|
{ |
|
if( m_iRadioMessages < 0 ) |
|
return; |
|
|
|
if( m_flRadioTime > gpGlobals->curtime ) |
|
return; |
|
|
|
m_iRadioMessages--; |
|
m_flRadioTime = gpGlobals->curtime + 1.5; |
|
|
|
switch ( slot ) |
|
{ |
|
case 1 : |
|
Radio( "Radio.CoverMe", "#Cstrike_TitlesTXT_Cover_me" ); |
|
break; |
|
|
|
case 2 : |
|
Radio( "Radio.YouTakeThePoint", "#Cstrike_TitlesTXT_You_take_the_point" ); |
|
break; |
|
|
|
case 3 : |
|
Radio( "Radio.HoldPosition", "#Cstrike_TitlesTXT_Hold_this_position" ); |
|
break; |
|
|
|
case 4 : |
|
Radio( "Radio.Regroup", "#Cstrike_TitlesTXT_Regroup_team" ); |
|
break; |
|
|
|
case 5 : |
|
Radio( "Radio.FollowMe", "#Cstrike_TitlesTXT_Follow_me" ); |
|
break; |
|
|
|
case 6 : |
|
Radio( "Radio.TakingFire", "#Cstrike_TitlesTXT_Taking_fire" ); |
|
break; |
|
} |
|
|
|
// tell bots about radio message |
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" ); |
|
if ( event ) |
|
{ |
|
event->SetInt("userid", GetUserID() ); |
|
event->SetInt("slot", RADIO_START_1 + slot ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
} |
|
|
|
void CCSPlayer::HandleMenu_Radio2( int slot ) |
|
{ |
|
if( m_iRadioMessages < 0 ) |
|
return; |
|
|
|
if( m_flRadioTime > gpGlobals->curtime ) |
|
return; |
|
|
|
m_iRadioMessages--; |
|
m_flRadioTime = gpGlobals->curtime + 1.5; |
|
|
|
switch ( slot ) |
|
{ |
|
case 1 : |
|
Radio( "Radio.GoGoGo", "#Cstrike_TitlesTXT_Go_go_go" ); |
|
break; |
|
|
|
case 2 : |
|
Radio( "Radio.TeamFallBack", "#Cstrike_TitlesTXT_Team_fall_back" ); |
|
break; |
|
|
|
case 3 : |
|
Radio( "Radio.StickTogether", "#Cstrike_TitlesTXT_Stick_together_team" ); |
|
break; |
|
|
|
case 4 : |
|
Radio( "Radio.GetInPosition", "#Cstrike_TitlesTXT_Get_in_position_and_wait" ); |
|
break; |
|
|
|
case 5 : |
|
Radio( "Radio.StormFront", "#Cstrike_TitlesTXT_Storm_the_front" ); |
|
break; |
|
|
|
case 6 : |
|
Radio( "Radio.ReportInTeam", "#Cstrike_TitlesTXT_Report_in_team" ); |
|
break; |
|
} |
|
|
|
// tell bots about radio message |
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" ); |
|
if ( event ) |
|
{ |
|
event->SetInt("userid", GetUserID() ); |
|
event->SetInt("slot", RADIO_START_2 + slot ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
} |
|
|
|
void CCSPlayer::HandleMenu_Radio3( int slot ) |
|
{ |
|
if( m_iRadioMessages < 0 ) |
|
return; |
|
|
|
if( m_flRadioTime > gpGlobals->curtime ) |
|
return; |
|
|
|
m_iRadioMessages--; |
|
m_flRadioTime = gpGlobals->curtime + 1.5; |
|
|
|
switch ( slot ) |
|
{ |
|
case 1 : |
|
if ( random->RandomInt( 0,1 ) ) |
|
Radio( "Radio.Affirmitive", "#Cstrike_TitlesTXT_Affirmative" ); |
|
else |
|
Radio( "Radio.Roger", "#Cstrike_TitlesTXT_Roger_that" ); |
|
|
|
break; |
|
|
|
case 2 : |
|
Radio( "Radio.EnemySpotted", "#Cstrike_TitlesTXT_Enemy_spotted" ); |
|
break; |
|
|
|
case 3 : |
|
Radio( "Radio.NeedBackup", "#Cstrike_TitlesTXT_Need_backup" ); |
|
break; |
|
|
|
case 4 : |
|
Radio( "Radio.SectorClear", "#Cstrike_TitlesTXT_Sector_clear" ); |
|
break; |
|
|
|
case 5 : |
|
Radio( "Radio.InPosition", "#Cstrike_TitlesTXT_In_position" ); |
|
break; |
|
|
|
case 6 : |
|
Radio( "Radio.ReportingIn", "#Cstrike_TitlesTXT_Reporting_in" ); |
|
break; |
|
|
|
case 7 : |
|
Radio( "Radio.GetOutOfThere", "#Cstrike_TitlesTXT_Get_out_of_there" ); |
|
break; |
|
|
|
case 8 : |
|
Radio( "Radio.Negative", "#Cstrike_TitlesTXT_Negative" ); |
|
break; |
|
|
|
case 9 : |
|
Radio( "Radio.EnemyDown", "#Cstrike_TitlesTXT_Enemy_down" ); |
|
break; |
|
} |
|
|
|
// tell bots about radio message |
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_radio" ); |
|
if ( event ) |
|
{ |
|
event->SetInt("userid", GetUserID() ); |
|
event->SetInt("slot", RADIO_START_3 + slot ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
} |
|
|
|
void UTIL_CSRadioMessage( IRecipientFilter& filter, int iClient, int msg_dest, const char *msg_name, const char *param1 = NULL, const char *param2 = NULL, const char *param3 = NULL, const char *param4 = NULL ) |
|
{ |
|
UserMessageBegin( filter, "RadioText" ); |
|
WRITE_BYTE( msg_dest ); |
|
WRITE_BYTE( iClient ); |
|
WRITE_STRING( msg_name ); |
|
|
|
if ( param1 ) |
|
WRITE_STRING( param1 ); |
|
else |
|
WRITE_STRING( "" ); |
|
|
|
if ( param2 ) |
|
WRITE_STRING( param2 ); |
|
else |
|
WRITE_STRING( "" ); |
|
|
|
if ( param3 ) |
|
WRITE_STRING( param3 ); |
|
else |
|
WRITE_STRING( "" ); |
|
|
|
if ( param4 ) |
|
WRITE_STRING( param4 ); |
|
else |
|
WRITE_STRING( "" ); |
|
|
|
MessageEnd(); |
|
} |
|
|
|
void CCSPlayer::ConstructRadioFilter( CRecipientFilter& filter ) |
|
{ |
|
filter.MakeReliable(); |
|
|
|
int localTeam = GetTeamNumber(); |
|
|
|
int i; |
|
for ( i = 1; i <= gpGlobals->maxClients; ++i ) |
|
{ |
|
CCSPlayer *player = static_cast<CCSPlayer *>( UTIL_PlayerByIndex( i ) ); |
|
if ( !player ) |
|
continue; |
|
|
|
// Skip players ignoring the radio |
|
if ( player->m_bIgnoreRadio ) |
|
continue; |
|
|
|
if( player->GetTeamNumber() == TEAM_SPECTATOR ) |
|
{ |
|
// add spectators |
|
if( player->m_iObserverMode == OBS_MODE_IN_EYE || player->m_iObserverMode == OBS_MODE_CHASE ) |
|
{ |
|
filter.AddRecipient( player ); |
|
} |
|
} |
|
else if( player->GetTeamNumber() == localTeam ) |
|
{ |
|
// add teammates |
|
filter.AddRecipient( player ); |
|
} |
|
} |
|
} |
|
|
|
void CCSPlayer::Radio( const char *pszRadioSound, const char *pszRadioText ) |
|
{ |
|
if( !IsAlive() ) |
|
return; |
|
|
|
if ( IsObserver() ) |
|
return; |
|
|
|
CRecipientFilter filter; |
|
ConstructRadioFilter( filter ); |
|
|
|
if( pszRadioText ) |
|
{ |
|
const char *pszLocationText = CSGameRules()->GetChatLocation( true, this ); |
|
if ( pszLocationText && *pszLocationText ) |
|
{ |
|
UTIL_CSRadioMessage( filter, entindex(), HUD_PRINTTALK, "#Game_radio_location", GetPlayerName(), pszLocationText, pszRadioText ); |
|
} |
|
else |
|
{ |
|
UTIL_CSRadioMessage( filter, entindex(), HUD_PRINTTALK, "#Game_radio", GetPlayerName(), pszRadioText ); |
|
} |
|
} |
|
|
|
UserMessageBegin ( filter, "SendAudio" ); |
|
WRITE_STRING( pszRadioSound ); |
|
MessageEnd(); |
|
|
|
//icon over the head for teammates |
|
TE_RadioIcon( filter, 0.0, this ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Outputs currently connected players to the console |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::ListPlayers() |
|
{ |
|
char buf[64]; |
|
for ( int i=1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) ); |
|
if ( pPlayer && !pPlayer->IsDormant() ) |
|
{ |
|
if ( pPlayer->IsBot() ) |
|
{ |
|
Q_snprintf( buf, sizeof(buf), "B %d : %s", pPlayer->GetUserID(), pPlayer->GetPlayerName() ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( buf, sizeof(buf), " %d : %s", pPlayer->GetUserID(), pPlayer->GetPlayerName() ); |
|
} |
|
ClientPrint( this, HUD_PRINTCONSOLE, buf ); |
|
} |
|
} |
|
ClientPrint( this, HUD_PRINTCONSOLE, "\n" ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : &info - |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::OnDamagedByExplosion( const CTakeDamageInfo &info ) |
|
{ |
|
float lastDamage = info.GetDamage(); |
|
|
|
//Adrian - This is hacky since we might have been damaged by something else |
|
//but since the round is ending, who cares. |
|
if ( CSGameRules()->m_bTargetBombed == true ) |
|
return; |
|
|
|
float distanceFromPlayer = 9999.0f; |
|
|
|
CBaseEntity *inflictor = info.GetInflictor(); |
|
if ( inflictor ) |
|
{ |
|
Vector delta = GetAbsOrigin() - inflictor->GetAbsOrigin(); |
|
distanceFromPlayer = delta.Length(); |
|
} |
|
|
|
bool shock = lastDamage >= 30.0f; |
|
|
|
if ( !shock ) |
|
return; |
|
|
|
m_applyDeafnessTime = gpGlobals->curtime + 0.3; |
|
m_currentDeafnessFilter = 0; |
|
} |
|
|
|
void CCSPlayer::ApplyDeafnessEffect() |
|
{ |
|
// what's happening here is that the low-pass filter and the oscillator frequency effects need |
|
// to fade in and out slowly. So we have several filters that we switch between to achieve this |
|
// effect. The first 3rd of the total effect will be the "fade in" of the effect. Which means going |
|
// from filter to filter from the first to the last. Then we keep on the "last" filter for another |
|
// third of the total effect time. Then the last third of the time we go back from the last filter |
|
// to the first. Clear as mud? |
|
|
|
// glossary: |
|
// filter: an individual filter as defined in dsp_presets.txt |
|
// section: one of the sections for the total effect, fade in, full, fade out are the possible sections |
|
// effect: the total effect of combining all the sections, the whole of what the player hears from start to finish. |
|
|
|
const int firstGrenadeFilterIndex = 137; |
|
const int lastGrenadeFilterIndex = 139; |
|
const float grenadeEffectLengthInSecs = 4.5f; // time of the total effect |
|
const float fadeInSectionTime = 0.1f; |
|
const float fadeOutSectionTime = 1.5f; |
|
|
|
const float timeForEachFilterInFadeIn = fadeInSectionTime / (lastGrenadeFilterIndex - firstGrenadeFilterIndex); |
|
const float timeForEachFilterInFadeOut = fadeOutSectionTime / (lastGrenadeFilterIndex - firstGrenadeFilterIndex); |
|
|
|
float timeIntoEffect = gpGlobals->curtime - m_applyDeafnessTime; |
|
|
|
if (timeIntoEffect >= grenadeEffectLengthInSecs) |
|
{ |
|
// the effect is done, so reset the deafness variables. |
|
m_applyDeafnessTime = 0.0f; |
|
m_currentDeafnessFilter = 0; |
|
return; |
|
} |
|
|
|
int section = 0; |
|
|
|
if (timeIntoEffect < fadeInSectionTime) |
|
{ |
|
section = 0; |
|
} |
|
else if (timeIntoEffect < (grenadeEffectLengthInSecs - fadeOutSectionTime)) |
|
{ |
|
section = 1; |
|
} |
|
else |
|
{ |
|
section = 2; |
|
} |
|
|
|
int filterToUse = 0; |
|
|
|
if (section == 0) |
|
{ |
|
// fade into the effect. |
|
int filterIndex = (int)(timeIntoEffect / timeForEachFilterInFadeIn); |
|
filterToUse = filterIndex += firstGrenadeFilterIndex; |
|
} |
|
else if (section == 1) |
|
{ |
|
// in full effect. |
|
filterToUse = lastGrenadeFilterIndex; |
|
} |
|
else if (section == 2) |
|
{ |
|
// fade out of the effect |
|
float timeIntoSection = timeIntoEffect - (grenadeEffectLengthInSecs - fadeOutSectionTime); |
|
int filterIndex = (int)(timeIntoSection / timeForEachFilterInFadeOut); |
|
filterToUse = lastGrenadeFilterIndex - filterIndex - 1; |
|
} |
|
|
|
if (filterToUse != m_currentDeafnessFilter) |
|
{ |
|
m_currentDeafnessFilter = filterToUse; |
|
|
|
CSingleUserRecipientFilter user( this ); |
|
enginesound->SetPlayerDSP( user, m_currentDeafnessFilter, false ); |
|
} |
|
} |
|
|
|
|
|
void CCSPlayer::NoteWeaponFired() |
|
{ |
|
Assert( m_pCurrentCommand ); |
|
if( m_pCurrentCommand ) |
|
{ |
|
m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number; |
|
} |
|
} |
|
|
|
|
|
bool CCSPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const |
|
{ |
|
// No need to lag compensate at all if we're not attacking in this command and |
|
// we haven't attacked recently. |
|
if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) ) |
|
{ |
|
if ( ( pCmd->buttons & IN_ATTACK2 ) == 0 ) |
|
return false; |
|
|
|
CWeaponCSBase *weapon = GetActiveCSWeapon(); |
|
if ( !weapon ) |
|
return false; |
|
|
|
if ( weapon->GetWeaponID() != WEAPON_KNIFE ) |
|
return false; // IN_ATTACK2 with WEAPON_KNIFE should do lag compensation |
|
} |
|
|
|
return BaseClass::WantsLagCompensationOnEntity( pPlayer, pCmd, pEntityTransmitBits ); |
|
} |
|
|
|
// Handles the special "radio" alias commands we're creating to accommodate the scripts players use |
|
// ** Returns true if we've handled the command ** |
|
bool HandleRadioAliasCommands( CCSPlayer *pPlayer, const char *pszCommand ) |
|
{ |
|
bool bRetVal = false; |
|
|
|
// don't execute them if we are not alive or are an observer |
|
if( !pPlayer->IsAlive() || pPlayer->IsObserver() ) |
|
return false; |
|
|
|
// Radio1 commands |
|
if ( FStrEq( pszCommand, "coverme" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio1( 1 ); |
|
} |
|
else if ( FStrEq( pszCommand, "takepoint" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio1( 2 ); |
|
} |
|
else if ( FStrEq( pszCommand, "holdpos" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio1( 3 ); |
|
} |
|
else if ( FStrEq( pszCommand, "regroup" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio1( 4 ); |
|
} |
|
else if ( FStrEq( pszCommand, "followme" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio1( 5 ); |
|
} |
|
else if ( FStrEq( pszCommand, "takingfire" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio1( 6 ); |
|
} |
|
// Radio2 commands |
|
else if ( FStrEq( pszCommand, "go" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio2( 1 ); |
|
} |
|
else if ( FStrEq( pszCommand, "fallback" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio2( 2 ); |
|
} |
|
else if ( FStrEq( pszCommand, "sticktog" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio2( 3 ); |
|
} |
|
else if ( FStrEq( pszCommand, "getinpos" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio2( 4 ); |
|
} |
|
else if ( FStrEq( pszCommand, "stormfront" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio2( 5 ); |
|
} |
|
else if ( FStrEq( pszCommand, "report" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio2( 6 ); |
|
} |
|
// Radio3 commands |
|
else if ( FStrEq( pszCommand, "roger" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 1 ); |
|
} |
|
else if ( FStrEq( pszCommand, "enemyspot" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 2 ); |
|
} |
|
else if ( FStrEq( pszCommand, "needbackup" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 3 ); |
|
} |
|
else if ( FStrEq( pszCommand, "sectorclear" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 4 ); |
|
} |
|
else if ( FStrEq( pszCommand, "inposition" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 5 ); |
|
} |
|
else if ( FStrEq( pszCommand, "reportingin" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 6 ); |
|
} |
|
else if ( FStrEq( pszCommand, "getout" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 7 ); |
|
} |
|
else if ( FStrEq( pszCommand, "negative" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 8 ); |
|
} |
|
else if ( FStrEq( pszCommand, "enemydown" ) ) |
|
{ |
|
bRetVal = true; |
|
pPlayer->HandleMenu_Radio3( 9 ); |
|
} |
|
|
|
return bRetVal; |
|
} |
|
|
|
bool CCSPlayer::ShouldRunRateLimitedCommand( const CCommand &args ) |
|
{ |
|
const char *pcmd = args[0]; |
|
|
|
int i = m_RateLimitLastCommandTimes.Find( pcmd ); |
|
if ( i == m_RateLimitLastCommandTimes.InvalidIndex() ) |
|
{ |
|
m_RateLimitLastCommandTimes.Insert( pcmd, gpGlobals->curtime ); |
|
return true; |
|
} |
|
else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i]) < CS_COMMAND_MAX_RATE ) |
|
{ |
|
// Too fast. |
|
return false; |
|
} |
|
else |
|
{ |
|
m_RateLimitLastCommandTimes[i] = gpGlobals->curtime; |
|
return true; |
|
} |
|
} |
|
|
|
bool CCSPlayer::ClientCommand( const CCommand &args ) |
|
{ |
|
const char *pcmd = args[0]; |
|
|
|
// Bots mimic our client commands. |
|
/* |
|
if ( bot_mimic.GetInt() && !( GetFlags() & FL_FAKECLIENT ) ) |
|
{ |
|
for ( int i=1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( i ) ); |
|
if ( pPlayer && pPlayer != this && ( pPlayer->GetFlags() & FL_FAKECLIENT ) ) |
|
{ |
|
pPlayer->ClientCommand( pcmd ); |
|
} |
|
} |
|
} |
|
*/ |
|
|
|
#if defined ( DEBUG ) |
|
|
|
if ( FStrEq( pcmd, "bot_cmd" ) ) |
|
{ |
|
CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( UTIL_PlayerByIndex( atoi( args[1] ) ) ); |
|
if ( pPlayer && pPlayer != this && ( pPlayer->GetFlags() & FL_FAKECLIENT ) ) |
|
{ |
|
CCommand botArgs( args.ArgC() - 2, &args.ArgV()[2] ); |
|
pPlayer->ClientCommand( botArgs ); |
|
pPlayer->RemoveEffects( EF_NODRAW ); |
|
} |
|
return true; |
|
} |
|
|
|
if ( FStrEq( pcmd, "blind" ) ) |
|
{ |
|
if ( ShouldRunRateLimitedCommand( args ) ) |
|
{ |
|
if ( args.ArgC() == 3 ) |
|
{ |
|
Blind( atof( args[1] ), atof( args[2] ) ); |
|
} |
|
else |
|
{ |
|
ClientPrint( this, HUD_PRINTCONSOLE, "usage: blind holdtime fadetime\n" ); |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
if ( FStrEq( pcmd, "deafen" ) ) |
|
{ |
|
Deafen( 0.0f ); |
|
return true; |
|
} |
|
|
|
if ( FStrEq( pcmd, "he_deafen" ) ) |
|
{ |
|
m_applyDeafnessTime = gpGlobals->curtime + 0.3; |
|
m_currentDeafnessFilter = 0; |
|
return true; |
|
} |
|
|
|
if ( FStrEq( pcmd, "hint_reset" ) ) |
|
{ |
|
m_iDisplayHistoryBits = 0; |
|
return true; |
|
} |
|
|
|
if ( FStrEq( pcmd, "punch" ) ) |
|
{ |
|
float flDamage = 100; |
|
|
|
QAngle punchAngle = GetPunchAngle(); |
|
|
|
punchAngle.x = flDamage * random->RandomFloat ( -0.15, 0.15 ); |
|
punchAngle.y = flDamage * random->RandomFloat ( -0.15, 0.15 ); |
|
punchAngle.z = flDamage * random->RandomFloat ( -0.15, 0.15 ); |
|
|
|
clamp( punchAngle.x, -4, punchAngle.x ); |
|
clamp( punchAngle.y, -5, 5 ); |
|
clamp( punchAngle.z, -5, 5 ); |
|
|
|
// +y == down |
|
// +x == left |
|
// +z == roll clockwise |
|
if ( args.ArgC() == 4 ) |
|
{ |
|
punchAngle.x = atof(args[1]); |
|
punchAngle.y = atof(args[2]); |
|
punchAngle.z = atof(args[3]); |
|
} |
|
|
|
SetPunchAngle( punchAngle ); |
|
|
|
return true; |
|
} |
|
|
|
#endif //DEBUG |
|
|
|
if ( FStrEq( pcmd, "jointeam" ) ) |
|
{ |
|
if ( args.ArgC() < 2 ) |
|
{ |
|
Warning( "Player sent bad jointeam syntax\n" ); |
|
} |
|
|
|
if ( ShouldRunRateLimitedCommand( args ) ) |
|
{ |
|
int iTeam = atoi( args[1] ); |
|
HandleCommand_JoinTeam( iTeam ); |
|
} |
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "spectate" ) ) |
|
{ |
|
if ( ShouldRunRateLimitedCommand( args ) ) |
|
{ |
|
// instantly join spectators |
|
HandleCommand_JoinTeam( TEAM_SPECTATOR ); |
|
} |
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "joingame" ) ) |
|
{ |
|
// player just closed MOTD dialog |
|
if ( m_iPlayerState == STATE_WELCOME ) |
|
{ |
|
State_Transition( STATE_PICKINGTEAM ); |
|
} |
|
|
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "joinclass" ) ) |
|
{ |
|
if ( args.ArgC() < 2 ) |
|
{ |
|
Warning( "Player sent bad joinclass syntax\n" ); |
|
} |
|
|
|
if ( ShouldRunRateLimitedCommand( args ) ) |
|
{ |
|
int iClass = atoi( args[1] ); |
|
HandleCommand_JoinClass( iClass ); |
|
} |
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "drop" ) ) |
|
{ |
|
CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() ); |
|
|
|
if( pWeapon ) |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Determine value of dropped item. |
|
//============================================================================= |
|
|
|
if ( !pWeapon->IsAPriorOwner( this ) ) |
|
{ |
|
pWeapon->AddToPriorOwnerList( this ); |
|
|
|
CCS_GameStats.IncrementStat(this, CSTAT_ITEMS_DROPPED_VALUE, pWeapon->GetCSWpnData().GetWeaponPrice()); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
CSWeaponType type = pWeapon->GetCSWpnData().m_WeaponType; |
|
|
|
if( type != WEAPONTYPE_KNIFE && type != WEAPONTYPE_GRENADE ) |
|
{ |
|
if (CSGameRules()->GetCanDonateWeapon() && !pWeapon->GetDonated()) |
|
{ |
|
pWeapon->SetDonated(true); |
|
pWeapon->SetDonor(this); |
|
} |
|
CSWeaponDrop( pWeapon, true, true ); |
|
|
|
} |
|
} |
|
|
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "buy" ) ) |
|
{ |
|
BuyResult_e result = BUY_INVALID_ITEM; |
|
if ( args.ArgC() == 2 ) |
|
{ |
|
result = HandleCommand_Buy( args[1] ); |
|
} |
|
if ( result == BUY_INVALID_ITEM ) |
|
{ |
|
// Print out a message on the console |
|
int msg_dest = HUD_PRINTCONSOLE; |
|
|
|
ClientPrint( this, msg_dest, "usage: buy <item>\n" ); |
|
ClientPrint( this, msg_dest, " primammo\n" ); |
|
ClientPrint( this, msg_dest, " secammo\n" ); |
|
ClientPrint( this, msg_dest, " vest\n" ); |
|
ClientPrint( this, msg_dest, " vesthelm\n" ); |
|
ClientPrint( this, msg_dest, " defuser\n" ); |
|
//ClientPrint( this, msg_dest, " shield\n" ); |
|
ClientPrint( this, msg_dest, " nvgs\n" ); |
|
ClientPrint( this, msg_dest, " flashbang\n" ); |
|
ClientPrint( this, msg_dest, " hegrenade\n" ); |
|
ClientPrint( this, msg_dest, " smokegrenade\n" ); |
|
ClientPrint( this, msg_dest, " galil\n" ); |
|
ClientPrint( this, msg_dest, " ak47\n" ); |
|
ClientPrint( this, msg_dest, " scout\n" ); |
|
ClientPrint( this, msg_dest, " sg552\n" ); |
|
ClientPrint( this, msg_dest, " awp\n" ); |
|
ClientPrint( this, msg_dest, " g3sg1\n" ); |
|
ClientPrint( this, msg_dest, " famas\n" ); |
|
ClientPrint( this, msg_dest, " m4a1\n" ); |
|
ClientPrint( this, msg_dest, " aug\n" ); |
|
ClientPrint( this, msg_dest, " sg550\n" ); |
|
ClientPrint( this, msg_dest, " glock\n" ); |
|
ClientPrint( this, msg_dest, " usp\n" ); |
|
ClientPrint( this, msg_dest, " p228\n" ); |
|
ClientPrint( this, msg_dest, " deagle\n" ); |
|
ClientPrint( this, msg_dest, " elite\n" ); |
|
ClientPrint( this, msg_dest, " fiveseven\n" ); |
|
ClientPrint( this, msg_dest, " m3\n" ); |
|
ClientPrint( this, msg_dest, " xm1014\n" ); |
|
ClientPrint( this, msg_dest, " mac10\n" ); |
|
ClientPrint( this, msg_dest, " tmp\n" ); |
|
ClientPrint( this, msg_dest, " mp5navy\n" ); |
|
ClientPrint( this, msg_dest, " ump45\n" ); |
|
ClientPrint( this, msg_dest, " p90\n" ); |
|
ClientPrint( this, msg_dest, " m249\n" ); |
|
} |
|
|
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "buyammo1" ) ) |
|
{ |
|
AttemptToBuyAmmoSingle(0); |
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "buyammo2" ) ) |
|
{ |
|
AttemptToBuyAmmoSingle(1); |
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "nightvision" ) ) |
|
{ |
|
if ( ShouldRunRateLimitedCommand( args ) ) |
|
{ |
|
if( m_bHasNightVision ) |
|
{ |
|
if( m_bNightVisionOn ) |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "Player.NightVisionOff" ); |
|
} |
|
else |
|
{ |
|
CPASAttenuationFilter filter( this ); |
|
EmitSound( filter, entindex(), "Player.NightVisionOn" ); |
|
} |
|
|
|
m_bNightVisionOn = !m_bNightVisionOn; |
|
} |
|
} |
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "menuselect" ) ) |
|
{ |
|
return true; |
|
} |
|
else if ( HandleRadioAliasCommands( this, pcmd ) ) |
|
{ |
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "listplayers" ) ) |
|
{ |
|
ListPlayers(); |
|
return true; |
|
} |
|
|
|
else if ( FStrEq( pcmd, "ignorerad" ) ) |
|
{ |
|
m_bIgnoreRadio = !m_bIgnoreRadio; |
|
if ( m_bIgnoreRadio ) |
|
{ |
|
ClientPrint( this, HUD_PRINTTALK, "#Ignore_Radio" ); |
|
} |
|
else |
|
{ |
|
ClientPrint( this, HUD_PRINTTALK, "#Accept_Radio" ); |
|
} |
|
return true; |
|
} |
|
else if ( FStrEq( pcmd, "become_vip" ) ) |
|
{ |
|
//MIKETODO: VIP mode |
|
/* |
|
if ( ( CSGameRules()->m_iMapHasVIPSafetyZone == 1 ) && ( m_iTeam == TEAM_CT ) ) |
|
{ |
|
mp->AddToVIPQueue( this ); |
|
} |
|
*/ |
|
return true; |
|
} |
|
|
|
return BaseClass::ClientCommand( args ); |
|
} |
|
|
|
|
|
// returns true if the selection has been handled and the player's menu |
|
// can be closed...false if the menu should be displayed again |
|
bool CCSPlayer::HandleCommand_JoinTeam( int team ) |
|
{ |
|
CCSGameRules *mp = CSGameRules(); |
|
|
|
if ( !GetGlobalTeam( team ) ) |
|
{ |
|
DevWarning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team ); |
|
return false; |
|
} |
|
|
|
// If this player is a VIP, don't allow him to switch teams/appearances unless the following conditions are met : |
|
// a) There is another TEAM_CT player who is in the queue to be a VIP |
|
// b) This player is dead |
|
|
|
//MIKETODO: handle this when doing VIP mode |
|
/* |
|
if ( m_bIsVIP == true ) |
|
{ |
|
if ( !IsDead() ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Switch_From_VIP" ); |
|
MenuReset(); |
|
return true; |
|
} |
|
else if ( mp->IsVIPQueueEmpty() == true ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Switch_From_VIP" ); |
|
MenuReset(); |
|
return true; |
|
} |
|
} |
|
|
|
//MIKETODO: VIP mode |
|
|
|
case 3: |
|
if ( ( mp->m_iMapHasVIPSafetyZone == 1 ) && ( m_iTeam == TEAM_CT ) ) |
|
{ |
|
mp->AddToVIPQueue( player ); |
|
MenuReset(); |
|
return true; |
|
} |
|
else |
|
{ |
|
return false; |
|
} |
|
break; |
|
*/ |
|
|
|
// If we already died and changed teams once, deny |
|
if( m_bTeamChanged && team != m_iOldTeam && team != TEAM_SPECTATOR ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#Only_1_Team_Change" ); |
|
return true; |
|
} |
|
|
|
// check if we're limited in our team selection |
|
if ( team == TEAM_UNASSIGNED && !IsBot() ) |
|
{ |
|
team = mp->GetHumanTeam(); // returns TEAM_UNASSIGNED if we're unrestricted |
|
} |
|
|
|
if ( team == TEAM_UNASSIGNED ) |
|
{ |
|
// Attempt to auto-select a team, may set team to T, CT or SPEC |
|
team = mp->SelectDefaultTeam( !IsBot() ); |
|
|
|
if ( team == TEAM_UNASSIGNED ) |
|
{ |
|
// still team unassigned, try to kick a bot if possible |
|
|
|
// kick a bot to allow human to join |
|
if (cv_bot_auto_vacate.GetBool() && !IsBot()) |
|
{ |
|
team = (random->RandomInt( 0, 1 ) == 0) ? TEAM_TERRORIST : TEAM_CT; |
|
if (UTIL_KickBotFromTeam( team ) == false) |
|
{ |
|
// no bots on that team, try the other |
|
team = (team == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT; |
|
if (UTIL_KickBotFromTeam( team ) == false) |
|
{ |
|
// couldn't kick any bots, fail |
|
team = TEAM_UNASSIGNED; |
|
} |
|
} |
|
} |
|
|
|
if (team == TEAM_UNASSIGNED) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#All_Teams_Full" ); |
|
ShowViewPortPanel( PANEL_TEAM ); |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
if ( team == GetTeamNumber() ) |
|
{ |
|
// Let people change class (skin) by re-joining the same team |
|
if ( GetTeamNumber() == TEAM_TERRORIST && TerroristPlayerModels.Count() > 1 ) |
|
{ |
|
ShowViewPortPanel( PANEL_CLASS_TER ); |
|
} |
|
else if ( GetTeamNumber() == TEAM_CT && CTPlayerModels.Count() > 1 ) |
|
{ |
|
ShowViewPortPanel( PANEL_CLASS_CT ); |
|
} |
|
return true; // we wouldn't change the team |
|
} |
|
|
|
if ( mp->TeamFull( team ) ) |
|
{ |
|
// attempt to kick a bot to make room for this player |
|
bool madeRoom = false; |
|
if (cv_bot_auto_vacate.GetBool() && !IsBot()) |
|
{ |
|
if (UTIL_KickBotFromTeam( team )) |
|
madeRoom = true; |
|
} |
|
|
|
if (!madeRoom) |
|
{ |
|
if ( team == TEAM_TERRORIST ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#Terrorists_Full" ); |
|
} |
|
else if ( team == TEAM_CT ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#CTs_Full" ); |
|
} |
|
|
|
ShowViewPortPanel( PANEL_TEAM ); |
|
return false; |
|
} |
|
} |
|
|
|
// check if humans are restricted to a single team (Tour of Duty, etc) |
|
if ( !IsBot() && team != TEAM_SPECTATOR) |
|
{ |
|
int humanTeam = mp->GetHumanTeam(); |
|
if ( humanTeam != TEAM_UNASSIGNED && humanTeam != team ) |
|
{ |
|
if ( humanTeam == TEAM_TERRORIST ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#Humans_Join_Team_T" ); |
|
} |
|
else if ( humanTeam == TEAM_CT ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#Humans_Join_Team_CT" ); |
|
} |
|
|
|
ShowViewPortPanel( PANEL_TEAM ); |
|
return false; |
|
} |
|
} |
|
|
|
if ( team == TEAM_SPECTATOR ) |
|
{ |
|
// Prevent this is the cvar is set |
|
if ( !mp_allowspectators.GetInt() && !IsHLTV() ) |
|
{ |
|
ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" ); |
|
return false; |
|
} |
|
|
|
if ( GetTeamNumber() != TEAM_UNASSIGNED && State_Get() == STATE_ACTIVE ) |
|
{ |
|
m_fNextSuicideTime = gpGlobals->curtime; // allow the suicide to work |
|
|
|
CommitSuicide(); |
|
|
|
// add 1 to frags to balance out the 1 subtracted for killing yourself |
|
IncrementFragCount( 1 ); |
|
} |
|
|
|
ChangeTeam( TEAM_SPECTATOR ); |
|
m_iClass = (int)CS_CLASS_NONE; |
|
|
|
if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_SPEC_DUCK; |
|
HintMessage( "#Spec_Duck", true, true ); |
|
} |
|
|
|
// do we have fadetoblack on? (need to fade their screen back in) |
|
if ( mp_fadetoblack.GetBool() ) |
|
{ |
|
color32_s clr = { 0,0,0,255 }; |
|
UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
// If the code gets this far, the team is not TEAM_UNASSIGNED |
|
|
|
|
|
if (mp->TeamStacked( team, GetTeamNumber() ))//players are allowed to change to their own team so they can just change their model |
|
{ |
|
// attempt to kick a bot to make room for this player |
|
bool madeRoom = false; |
|
if (cv_bot_auto_vacate.GetBool() && !IsBot()) |
|
{ |
|
if (UTIL_KickBotFromTeam( team )) |
|
madeRoom = true; |
|
} |
|
|
|
if (!madeRoom) |
|
{ |
|
// The specified team is full |
|
ClientPrint( |
|
this, |
|
HUD_PRINTCENTER, |
|
( team == TEAM_TERRORIST ) ? "#Too_Many_Terrorists" : "#Too_Many_CTs" ); |
|
|
|
ShowViewPortPanel( PANEL_TEAM ); |
|
return false; |
|
} |
|
} |
|
|
|
// Show the appropriate Choose Appearance menu |
|
// This must come before ClientKill() for CheckWinConditions() to function properly |
|
|
|
// Switch their actual team... |
|
ChangeTeam( team ); |
|
|
|
return true; |
|
} |
|
|
|
|
|
bool CCSPlayer::HandleCommand_JoinClass( int iClass ) |
|
{ |
|
if( iClass == CS_CLASS_NONE ) |
|
{ |
|
// User choosed random class |
|
switch ( GetTeamNumber() ) |
|
{ |
|
case TEAM_TERRORIST : iClass = RandomInt(FIRST_T_CLASS, LAST_T_CLASS); |
|
break; |
|
|
|
case TEAM_CT : iClass = RandomInt(FIRST_CT_CLASS, LAST_CT_CLASS); |
|
break; |
|
|
|
default : iClass = CS_CLASS_NONE; |
|
break; |
|
} |
|
} |
|
|
|
// clamp to valid classes |
|
switch ( GetTeamNumber() ) |
|
{ |
|
case TEAM_TERRORIST: |
|
iClass = clamp( iClass, FIRST_T_CLASS, LAST_T_CLASS ); |
|
break; |
|
case TEAM_CT: |
|
iClass = clamp( iClass, FIRST_CT_CLASS, LAST_CT_CLASS ); |
|
break; |
|
default: |
|
iClass = CS_CLASS_NONE; |
|
} |
|
|
|
// Reset the player's state |
|
if ( State_Get() == STATE_ACTIVE ) |
|
{ |
|
CSGameRules()->CheckWinConditions(); |
|
} |
|
|
|
if ( !IsBot() && State_Get() == STATE_ACTIVE ) // Bots are responsible about only switching classes when they join. |
|
{ |
|
// Kill player if switching classes while alive. |
|
// This mimics goldsrc CS 1.6, and prevents a player from hiding, and switching classes to |
|
// make the opposing team think there are more enemies than there really are. |
|
CommitSuicide(); |
|
} |
|
|
|
m_iClass = iClass; |
|
|
|
if (State_Get() == STATE_PICKINGCLASS) |
|
{ |
|
// SetModelFromClass(); |
|
GetIntoGame(); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
/* |
|
void CheckStartMoney( void ) |
|
{ |
|
if ( mp_startmoney.GetInt() > 16000 ) |
|
{ |
|
mp_startmoney.SetInt( 16000 ); |
|
} |
|
else if ( mp_startmoney.GetInt() < 800 ) |
|
{ |
|
mp_startmoney.SetInt( 800 ); |
|
} |
|
} |
|
*/ |
|
|
|
void CCSPlayer::GetIntoGame() |
|
{ |
|
// Set their model and if they're allowed to spawn right now, put them into the world. |
|
//SetPlayerModel( iClass ); |
|
|
|
SetFOV( this, 0 ); |
|
m_flLastMovement = gpGlobals->curtime; |
|
|
|
CCSGameRules *MPRules = CSGameRules(); |
|
|
|
/* //MIKETODO: Escape gameplay ? |
|
if ( ( MPRules->m_bMapHasEscapeZone == true ) && ( m_iTeam == TEAM_CT ) ) |
|
{ |
|
m_iAccount = 0; |
|
|
|
CheckStartMoney(); |
|
AddAccount( (int)startmoney.value, true ); |
|
} |
|
*/ |
|
|
|
|
|
//****************New Code by SupraFiend************ |
|
if ( !MPRules->FPlayerCanRespawn( this ) ) |
|
{ |
|
// This player is joining in the middle of a round or is an observer. Put them directly into observer mode. |
|
//pev->deadflag = DEAD_RESPAWNABLE; |
|
//pev->classname = MAKE_STRING("player"); |
|
//pev->flags &= ( FL_PROXY | FL_FAKECLIENT ); // clear flags, but keep proxy and bot flags that might already be set |
|
//pev->flags |= FL_CLIENT | FL_SPECTATOR; |
|
//SetThink(PlayerDeathThink); |
|
if ( !(m_iDisplayHistoryBits & DHF_SPEC_DUCK) ) |
|
{ |
|
m_iDisplayHistoryBits |= DHF_SPEC_DUCK; |
|
HintMessage( "#Spec_Duck", true, true ); |
|
} |
|
|
|
State_Transition( STATE_OBSERVER_MODE ); |
|
|
|
m_wasNotKilledNaturally = true; |
|
|
|
MPRules->CheckWinConditions(); |
|
} |
|
else// else spawn them right in |
|
{ |
|
State_Transition( STATE_ACTIVE ); |
|
|
|
Spawn(); |
|
|
|
MPRules->CheckWinConditions(); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Have the rules update anything related to a player spawning in late |
|
//============================================================================= |
|
|
|
MPRules->SpawningLatePlayer(this); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
if( MPRules->m_flRestartRoundTime == 0.0f ) |
|
{ |
|
//Bomb target, no bomber and no bomb lying around. |
|
if( MPRules->IsBombDefuseMap() && !MPRules->IsThereABomber() && !MPRules->IsThereABomb() ) |
|
MPRules->GiveC4(); //Checks for terrorists. |
|
} |
|
|
|
// If a new terrorist is entering the fray, then up the # of potential escapers. |
|
if ( GetTeamNumber() == TEAM_TERRORIST ) |
|
MPRules->m_iNumEscapers++; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Reset Round Based Achievement Variables |
|
//============================================================================= |
|
|
|
ResetRoundBasedAchievementVariables(); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
} |
|
} |
|
|
|
|
|
int CCSPlayer::PlayerClass() const |
|
{ |
|
return m_iClass; |
|
} |
|
|
|
|
|
|
|
bool CCSPlayer::SelectSpawnSpot( const char *pEntClassName, CBaseEntity* &pSpot ) |
|
{ |
|
// Find the next spawn spot. |
|
pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName ); |
|
|
|
if ( pSpot == NULL ) // skip over the null point |
|
pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName ); |
|
|
|
CBaseEntity *pFirstSpot = pSpot; |
|
do |
|
{ |
|
if ( pSpot ) |
|
{ |
|
// check if pSpot is valid |
|
if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) ) |
|
{ |
|
if ( pSpot->GetAbsOrigin() == Vector( 0, 0, 0 ) ) |
|
{ |
|
pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName ); |
|
continue; |
|
} |
|
|
|
// if so, go to pSpot |
|
return true; |
|
} |
|
} |
|
// increment pSpot |
|
pSpot = gEntList.FindEntityByClassname( pSpot, pEntClassName ); |
|
} while ( pSpot != pFirstSpot ); // loop if we're not back to the start |
|
|
|
DevMsg("CCSPlayer::SelectSpawnSpot: couldn't find valid spawn point.\n"); |
|
|
|
return true; |
|
} |
|
|
|
|
|
CBaseEntity* CCSPlayer::EntSelectSpawnPoint() |
|
{ |
|
CBaseEntity *pSpot; |
|
|
|
/* MIKETODO: VIP |
|
// VIP spawn point ************* |
|
if ( ( g_pGameRules->IsDeathmatch() ) && ( ((CBasePlayer*)pPlayer)->m_bIsVIP == TRUE) ) |
|
{ |
|
//ALERT (at_console,"Looking for a VIP spawn point\n"); |
|
// Randomize the start spot |
|
//for ( int i = RANDOM_LONG(1,5); i > 0; i-- ) |
|
pSpot = UTIL_FindEntityByClassname( NULL, "info_vip_start" ); |
|
if ( !FNullEnt( pSpot ) ) // skip over the null point |
|
goto ReturnSpot; |
|
else |
|
goto CTSpawn; |
|
} |
|
|
|
// |
|
// the counter-terrorist spawns at "info_player_start" |
|
else |
|
*/ |
|
|
|
pSpot = NULL; |
|
if ( CSGameRules()->IsLogoMap() ) |
|
{ |
|
// This is a logo map. Don't allow movement or logos or menus. |
|
SelectSpawnSpot( "info_player_logo", pSpot ); |
|
LockPlayerInPlace(); |
|
goto ReturnSpot; |
|
} |
|
else |
|
{ |
|
if ( GetTeamNumber() == TEAM_CT ) |
|
{ |
|
pSpot = g_pLastCTSpawn; |
|
if ( SelectSpawnSpot( "info_player_counterterrorist", pSpot )) |
|
{ |
|
|
|
g_pLastCTSpawn = pSpot; |
|
goto ReturnSpot; |
|
} |
|
} |
|
|
|
/*********************************************************/ |
|
// The terrorist spawn points |
|
else if ( GetTeamNumber() == TEAM_TERRORIST ) |
|
{ |
|
pSpot = g_pLastTerroristSpawn; |
|
|
|
if ( SelectSpawnSpot( "info_player_terrorist", pSpot ) ) |
|
{ |
|
g_pLastTerroristSpawn = pSpot; |
|
goto ReturnSpot; |
|
} |
|
} |
|
} |
|
|
|
|
|
// If startspot is set, (re)spawn there. |
|
if ( !gpGlobals->startspot || !strlen(STRING(gpGlobals->startspot))) |
|
{ |
|
pSpot = gEntList.FindEntityByClassname(NULL, "info_player_terrorist"); |
|
if ( pSpot ) |
|
goto ReturnSpot; |
|
} |
|
else |
|
{ |
|
pSpot = gEntList.FindEntityByTarget( NULL, STRING(gpGlobals->startspot) ); |
|
if ( pSpot ) |
|
goto ReturnSpot; |
|
} |
|
|
|
ReturnSpot: |
|
if ( !pSpot ) |
|
{ |
|
if( CSGameRules()->IsLogoMap() ) |
|
Warning( "PutClientInServer: no info_player_logo on level\n" ); |
|
else |
|
Warning( "PutClientInServer: no info_player_start on level\n" ); |
|
|
|
return CBaseEntity::Instance( INDEXENT(0) ); |
|
} |
|
|
|
return pSpot; |
|
} |
|
|
|
|
|
void CCSPlayer::SetProgressBarTime( int barTime ) |
|
{ |
|
m_iProgressBarDuration = barTime; |
|
m_flProgressBarStartTime = this->m_flSimulationTime; |
|
} |
|
|
|
|
|
void CCSPlayer::PlayerDeathThink() |
|
{ |
|
} |
|
|
|
|
|
void CCSPlayer::State_Transition( CSPlayerState newState ) |
|
{ |
|
State_Leave(); |
|
State_Enter( newState ); |
|
} |
|
|
|
|
|
void CCSPlayer::State_Enter( CSPlayerState newState ) |
|
{ |
|
m_iPlayerState = newState; |
|
m_pCurStateInfo = State_LookupInfo( newState ); |
|
|
|
if ( cs_ShowStateTransitions.GetInt() == -1 || cs_ShowStateTransitions.GetInt() == entindex() ) |
|
{ |
|
if ( m_pCurStateInfo ) |
|
Msg( "ShowStateTransitions: entering '%s'\n", m_pCurStateInfo->m_pStateName ); |
|
else |
|
Msg( "ShowStateTransitions: entering #%d\n", newState ); |
|
} |
|
|
|
// Initialize the new state. |
|
if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState ) |
|
(this->*m_pCurStateInfo->pfnEnterState)(); |
|
} |
|
|
|
|
|
void CCSPlayer::State_Leave() |
|
{ |
|
if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState ) |
|
{ |
|
(this->*m_pCurStateInfo->pfnLeaveState)(); |
|
} |
|
} |
|
|
|
|
|
void CCSPlayer::State_PreThink() |
|
{ |
|
if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink ) |
|
{ |
|
(this->*m_pCurStateInfo->pfnPreThink)(); |
|
} |
|
} |
|
|
|
|
|
CCSPlayerStateInfo* CCSPlayer::State_LookupInfo( CSPlayerState state ) |
|
{ |
|
// This table MUST match the |
|
static CCSPlayerStateInfo playerStateInfos[] = |
|
{ |
|
{ STATE_ACTIVE, "STATE_ACTIVE", &CCSPlayer::State_Enter_ACTIVE, NULL, &CCSPlayer::State_PreThink_ACTIVE }, |
|
{ STATE_WELCOME, "STATE_WELCOME", &CCSPlayer::State_Enter_WELCOME, NULL, &CCSPlayer::State_PreThink_WELCOME }, |
|
{ STATE_PICKINGTEAM, "STATE_PICKINGTEAM", &CCSPlayer::State_Enter_PICKINGTEAM, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE }, |
|
{ STATE_PICKINGCLASS, "STATE_PICKINGCLASS", &CCSPlayer::State_Enter_PICKINGCLASS, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE }, |
|
{ STATE_DEATH_ANIM, "STATE_DEATH_ANIM", &CCSPlayer::State_Enter_DEATH_ANIM, NULL, &CCSPlayer::State_PreThink_DEATH_ANIM }, |
|
{ STATE_DEATH_WAIT_FOR_KEY, "STATE_DEATH_WAIT_FOR_KEY", &CCSPlayer::State_Enter_DEATH_WAIT_FOR_KEY, NULL, &CCSPlayer::State_PreThink_DEATH_WAIT_FOR_KEY }, |
|
{ STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CCSPlayer::State_Enter_OBSERVER_MODE, NULL, &CCSPlayer::State_PreThink_OBSERVER_MODE } |
|
}; |
|
|
|
for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ ) |
|
{ |
|
if ( playerStateInfos[i].m_iPlayerState == state ) |
|
return &playerStateInfos[i]; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
|
|
void CCSPlayer::PhysObjectSleep() |
|
{ |
|
IPhysicsObject *pObj = VPhysicsGetObject(); |
|
if ( pObj ) |
|
pObj->Sleep(); |
|
} |
|
|
|
|
|
void CCSPlayer::PhysObjectWake() |
|
{ |
|
IPhysicsObject *pObj = VPhysicsGetObject(); |
|
if ( pObj ) |
|
pObj->Wake(); |
|
} |
|
|
|
|
|
void CCSPlayer::State_Enter_WELCOME() |
|
{ |
|
StartObserverMode( OBS_MODE_ROAMING ); |
|
|
|
// Important to set MOVETYPE_NONE or our physics object will fall while we're sitting at one of the intro cameras. |
|
SetMoveType( MOVETYPE_NONE ); |
|
AddSolidFlags( FSOLID_NOT_SOLID ); |
|
|
|
PhysObjectSleep(); |
|
|
|
const ConVar *hostname = cvar->FindVar( "hostname" ); |
|
const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY"; |
|
|
|
// Show info panel (if it's not a simple demo map). |
|
if ( !CSGameRules()->IsLogoMap() ) |
|
{ |
|
if ( CommandLine()->FindParm( "-makereslists" ) ) // don't show the MOTD when making reslists |
|
{ |
|
engine->ClientCommand( edict(), "jointeam 3\n" ); |
|
} |
|
else |
|
{ |
|
KeyValues *data = new KeyValues("data"); |
|
data->SetString( "title", title ); // info panel title |
|
data->SetString( "type", "1" ); // show userdata from stringtable entry |
|
data->SetString( "msg", "motd" ); // use this stringtable entry |
|
data->SetInt( "cmd", TEXTWINDOW_CMD_JOINGAME ); // exec this command if panel closed |
|
data->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() ); |
|
|
|
ShowViewPortPanel( PANEL_INFO, true, data ); |
|
|
|
data->deleteThis(); |
|
} |
|
} |
|
} |
|
|
|
|
|
void CCSPlayer::State_PreThink_WELCOME() |
|
{ |
|
// Verify some state. |
|
Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) ); |
|
Assert( GetAbsVelocity().Length() == 0 ); |
|
|
|
// Update whatever intro camera it's at. |
|
if( m_pIntroCamera && (gpGlobals->curtime >= m_fIntroCamTime) ) |
|
{ |
|
MoveToNextIntroCamera(); |
|
} |
|
} |
|
|
|
|
|
void CCSPlayer::State_Enter_PICKINGTEAM() |
|
{ |
|
ShowViewPortPanel( "team" ); // show the team menu |
|
} |
|
|
|
|
|
void CCSPlayer::State_Enter_DEATH_ANIM() |
|
{ |
|
if ( HasWeapons() ) |
|
{ |
|
// we drop the guns here because weapons that have an area effect and can kill their user |
|
// will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the |
|
// player class sometimes is freed. It's safer to manipulate the weapons once we know |
|
// we aren't calling into any of their code anymore through the player pointer. |
|
PackDeadPlayerItems(); |
|
} |
|
|
|
// Used for a timer. |
|
m_flDeathTime = gpGlobals->curtime; |
|
|
|
m_bAbortFreezeCam = false; |
|
|
|
StartObserverMode( OBS_MODE_DEATHCAM ); // go to observer mode |
|
RemoveEffects( EF_NODRAW ); // still draw player body |
|
|
|
if ( mp_fadetoblack.GetBool() ) |
|
{ |
|
color32_s clr = {0,0,0,255}; |
|
UTIL_ScreenFade( this, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT ); |
|
//Don't perform any freezecam stuff if we are fading to black |
|
State_Transition( STATE_DEATH_WAIT_FOR_KEY ); |
|
} |
|
} |
|
|
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish, pfreese] Added freeze cam logic |
|
//============================================================================= |
|
|
|
void CCSPlayer::State_PreThink_DEATH_ANIM() |
|
{ |
|
// If the anim is done playing, go to the next state (waiting for a keypress to |
|
// either respawn the guy or put him into observer mode). |
|
if ( GetFlags() & FL_ONGROUND ) |
|
{ |
|
float flForward = GetAbsVelocity().Length() - 20; |
|
if (flForward <= 0) |
|
{ |
|
SetAbsVelocity( vec3_origin ); |
|
} |
|
else |
|
{ |
|
Vector vAbsVel = GetAbsVelocity(); |
|
VectorNormalize( vAbsVel ); |
|
vAbsVel *= flForward; |
|
SetAbsVelocity( vAbsVel ); |
|
} |
|
} |
|
|
|
float fDeathEnd = m_flDeathTime + CS_DEATH_ANIMATION_TIME; |
|
float fFreezeEnd = fDeathEnd + spec_freeze_traveltime.GetFloat() + spec_freeze_time.GetFloat(); |
|
|
|
// transition to Freezecam mode once the death animation is complete |
|
if ( gpGlobals->curtime >= fDeathEnd ) |
|
{ |
|
if ( GetObserverTarget() && GetObserverTarget() != this && |
|
!m_bAbortFreezeCam && gpGlobals->curtime < fFreezeEnd && GetObserverMode() != OBS_MODE_FREEZECAM) |
|
{ |
|
StartObserverMode( OBS_MODE_FREEZECAM ); |
|
} |
|
else if(GetObserverMode() == OBS_MODE_FREEZECAM) |
|
{ |
|
if ( m_bAbortFreezeCam && !mp_fadetoblack.GetBool() ) |
|
{ |
|
State_Transition( STATE_OBSERVER_MODE ); |
|
} |
|
} |
|
} |
|
|
|
// Don't transfer to observer state until the freeze cam is done |
|
if ( gpGlobals->curtime < fFreezeEnd ) |
|
return; |
|
|
|
State_Transition( STATE_OBSERVER_MODE ); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
|
|
void CCSPlayer::State_Enter_DEATH_WAIT_FOR_KEY() |
|
{ |
|
// Remember when we died, so we can automatically put them into observer mode |
|
// if they don't hit a key soon enough. |
|
|
|
m_lifeState = LIFE_DEAD; |
|
|
|
StopAnimation(); |
|
|
|
// Don't do this. The ragdoll system expects to be able to read from this player on |
|
// the next update and will read it at the new origin if this is set. |
|
// Since it is more complicated to redesign the ragdoll system to not need that data |
|
// it is easier to cause a less obvious bug than popping ragdolls |
|
//AddEffects( EF_NOINTERP ); |
|
} |
|
|
|
|
|
void CCSPlayer::State_PreThink_DEATH_WAIT_FOR_KEY() |
|
{ |
|
// once we're done animating our death and we're on the ground, we want to set movetype to None so our dead body won't do collisions and stuff anymore |
|
// this prevents a bug where the dead body would go to a player's head if he walked over it while the dead player was clicking their button to respawn |
|
if ( GetMoveType() != MOVETYPE_NONE && (GetFlags() & FL_ONGROUND) ) |
|
SetMoveType( MOVETYPE_NONE ); |
|
|
|
// 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. |
|
|
|
bool fAnyButtonDown = (m_nButtons & ~IN_SCORE) != 0; |
|
if ( mp_fadetoblack.GetBool() ) |
|
fAnyButtonDown = false; |
|
|
|
// after a certain amount of time switch to observer mode even if they don't press a key. |
|
if (gpGlobals->curtime >= (m_flDeathTime + DEATH_ANIMATION_TIME + 3.0)) |
|
{ |
|
fAnyButtonDown = true; |
|
} |
|
|
|
if ( fAnyButtonDown ) |
|
{ |
|
if ( GetObserverTarget() ) |
|
{ |
|
StartReplayMode( 8, 8, GetObserverTarget()->entindex() ); |
|
} |
|
|
|
State_Transition( STATE_OBSERVER_MODE ); |
|
} |
|
} |
|
|
|
void CCSPlayer::State_Enter_OBSERVER_MODE() |
|
{ |
|
// do we have fadetoblack on? (need to fade their screen back in) |
|
if ( mp_fadetoblack.GetBool() && mp_forcecamera.GetInt() != OBS_ALLOW_NONE) |
|
{ |
|
color32_s clr = { 0,0,0,255 }; |
|
UTIL_ScreenFade( this, clr, 0, 0, FFADE_IN | FFADE_PURGE ); |
|
} |
|
|
|
int observerMode = m_iObserverLastMode; |
|
if ( IsNetClient() ) |
|
{ |
|
const char *pIdealMode = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_spec_mode" ); |
|
if ( pIdealMode ) |
|
{ |
|
int nIdealMode = atoi( pIdealMode ); |
|
|
|
if ( nIdealMode < OBS_MODE_IN_EYE ) |
|
{ |
|
nIdealMode = OBS_MODE_IN_EYE; |
|
} |
|
else if ( nIdealMode > OBS_MODE_ROAMING ) |
|
{ |
|
nIdealMode = OBS_MODE_ROAMING; |
|
} |
|
|
|
observerMode = nIdealMode; |
|
} |
|
} |
|
|
|
StartObserverMode( observerMode ); |
|
|
|
PhysObjectSleep(); |
|
} |
|
|
|
void CCSPlayer::State_PreThink_OBSERVER_MODE() |
|
{ |
|
// Make sure nobody has changed any of our state. |
|
// Assert( GetMoveType() == MOVETYPE_FLY ); |
|
Assert( m_takedamage == DAMAGE_NO ); |
|
Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) ); |
|
// Assert( IsEffectActive( EF_NODRAW ) ); |
|
|
|
// Must be dead. |
|
Assert( m_lifeState == LIFE_DEAD ); |
|
Assert( pl.deadflag ); |
|
} |
|
|
|
|
|
void CCSPlayer::State_Enter_PICKINGCLASS() |
|
{ |
|
if ( CommandLine()->FindParm( "-makereslists" ) ) // don't show the menu when making reslists |
|
{ |
|
engine->ClientCommand( edict(), "joinclass 0\n" ); |
|
return; |
|
} |
|
|
|
// go to spec mode, if dying keep deathcam |
|
if ( GetObserverMode() == OBS_MODE_DEATHCAM ) |
|
{ |
|
StartObserverMode( OBS_MODE_DEATHCAM ); |
|
} |
|
else |
|
{ |
|
StartObserverMode( OBS_MODE_FIXED ); |
|
} |
|
|
|
m_iClass = (int)CS_CLASS_NONE; |
|
|
|
PhysObjectSleep(); |
|
|
|
// show the class menu: |
|
if ( GetTeamNumber() == TEAM_TERRORIST && TerroristPlayerModels.Count() > 1 ) |
|
{ |
|
ShowViewPortPanel( PANEL_CLASS_TER ); |
|
} |
|
else if ( GetTeamNumber() == TEAM_CT && CTPlayerModels.Count() > 1 ) |
|
{ |
|
ShowViewPortPanel( PANEL_CLASS_CT ); |
|
} |
|
else |
|
{ |
|
HandleCommand_JoinClass( 0 ); |
|
} |
|
} |
|
|
|
void CCSPlayer::State_Enter_ACTIVE() |
|
{ |
|
SetMoveType( MOVETYPE_WALK ); |
|
RemoveSolidFlags( FSOLID_NOT_SOLID ); |
|
m_Local.m_iHideHUD = 0; |
|
PhysObjectWake(); |
|
} |
|
|
|
|
|
void CCSPlayer::State_PreThink_ACTIVE() |
|
{ |
|
// We only allow noclip here only because noclip is useful for debugging. |
|
// It would be nice if the noclip command set some flag so we could tell that they |
|
// did it intentionally. |
|
if ( IsEFlagSet( EFL_NOCLIP_ACTIVE ) ) |
|
{ |
|
// Assert( GetMoveType() == MOVETYPE_NOCLIP ); |
|
} |
|
else |
|
{ |
|
// Assert( GetMoveType() == MOVETYPE_WALK ); |
|
} |
|
|
|
Assert( !IsSolidFlagSet( FSOLID_NOT_SOLID ) ); |
|
} |
|
|
|
|
|
void CCSPlayer::Weapon_Equip( CBaseCombatWeapon *pWeapon ) |
|
{ |
|
CWeaponCSBase *pCSWeapon = dynamic_cast< CWeaponCSBase* >( pWeapon ); |
|
if ( pCSWeapon ) |
|
{ |
|
// For rifles, pistols, or the knife, drop our old weapon in this slot. |
|
if ( pCSWeapon->GetSlot() == WEAPON_SLOT_RIFLE || |
|
pCSWeapon->GetSlot() == WEAPON_SLOT_PISTOL || |
|
pCSWeapon->GetSlot() == WEAPON_SLOT_KNIFE ) |
|
{ |
|
CBaseCombatWeapon *pDropWeapon = Weapon_GetSlot( pCSWeapon->GetSlot() ); |
|
if ( pDropWeapon ) |
|
{ |
|
CSWeaponDrop( pDropWeapon, false, true ); |
|
} |
|
} |
|
else if( pCSWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_GRENADE ) |
|
{ |
|
//if we already have this weapon, just add the ammo and destroy it |
|
if( Weapon_OwnsThisType( pCSWeapon->GetClassname() ) ) |
|
{ |
|
Weapon_EquipAmmoOnly( pWeapon ); |
|
UTIL_Remove( pCSWeapon ); |
|
return; |
|
} |
|
} |
|
|
|
pCSWeapon->SetSolidFlags( FSOLID_NOT_SOLID ); |
|
pCSWeapon->SetOwnerEntity( this ); |
|
} |
|
|
|
BaseClass::Weapon_Equip( pWeapon ); |
|
} |
|
|
|
bool CCSPlayer::Weapon_CanUse( CBaseCombatWeapon *pBaseWeapon ) |
|
{ |
|
CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon ); |
|
|
|
if ( pWeapon ) |
|
{ |
|
// Don't give weapon_c4 to non-terrorists |
|
if( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_C4 && GetTeamNumber() != TEAM_TERRORIST ) |
|
{ |
|
return false; |
|
} |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool CCSPlayer::BumpWeapon( CBaseCombatWeapon *pBaseWeapon ) |
|
{ |
|
CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon ); |
|
if ( !pWeapon ) |
|
{ |
|
Assert( !pWeapon ); |
|
pBaseWeapon->AddSolidFlags( FSOLID_NOT_SOLID ); |
|
pBaseWeapon->AddEffects( EF_NODRAW ); |
|
Weapon_Equip( pBaseWeapon ); |
|
return true; |
|
} |
|
|
|
CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); |
|
|
|
// Can I have this weapon type? |
|
if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) |
|
{ |
|
extern int gEvilImpulse101; |
|
if ( gEvilImpulse101 ) |
|
{ |
|
UTIL_Remove( pWeapon ); |
|
} |
|
return false; |
|
} |
|
|
|
// Even if we already have a grenade in this slot, we can pickup another one if we don't already |
|
// own this type of grenade. |
|
bool bPickupGrenade = ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_GRENADE ); |
|
|
|
|
|
/* |
|
// ---------------------------------------- |
|
// If I already have it just take the ammo |
|
// ---------------------------------------- |
|
if ( !bPickupGrenade && Weapon_SlotOccupied( pWeapon ) ) |
|
{ |
|
Weapon_EquipAmmoOnly( pWeapon ); |
|
// Only remove me if I have no ammo left |
|
// Can't just check HasAnyAmmo because if I don't use clips, I want to be removed, |
|
if ( pWeapon->UsesClipsForAmmo1() && pWeapon->HasPrimaryAmmo() ) |
|
return false; |
|
|
|
UTIL_Remove( pWeapon ); |
|
return false; |
|
} |
|
*/ |
|
|
|
if ( HasShield() && pWeapon->GetCSWpnData().m_bCanUseWithShield == false ) |
|
return false; |
|
|
|
// Check ammo counts for grenades, and don't try to pick up more grenades than we can carry |
|
if ( bPickupGrenade ) |
|
{ |
|
CBaseCombatWeapon *pOwnedGrenade = Weapon_OwnsThisType( pWeapon->GetClassname() ); |
|
|
|
if( pOwnedGrenade ) |
|
{ |
|
int numGrenades = 0; |
|
int maxGrenades = 0; |
|
|
|
int ammoIndex = pOwnedGrenade->GetPrimaryAmmoType(); |
|
if( ammoIndex != -1 ) |
|
{ |
|
numGrenades = GetAmmoCount( ammoIndex ); |
|
} |
|
maxGrenades = GetAmmoDef()->MaxCarry(ammoIndex); |
|
|
|
if( numGrenades >= maxGrenades ) |
|
{ |
|
return false; |
|
} |
|
} |
|
} |
|
|
|
if( bPickupGrenade || !Weapon_SlotOccupied( pWeapon ) ) |
|
{ |
|
pWeapon->CheckRespawn(); |
|
|
|
pWeapon->AddSolidFlags( FSOLID_NOT_SOLID ); |
|
pWeapon->AddEffects( EF_NODRAW ); |
|
|
|
CCSPlayer* pDonor = pWeapon->GetDonor(); |
|
if ( pDonor && pDonor != this && pWeapon->GetCSWpnData().GetWeaponPrice() > m_iAccount ) |
|
{ |
|
CCS_GameStats.Event_PlayerDonatedWeapon( pDonor ); |
|
} |
|
pWeapon->SetDonor(NULL); |
|
|
|
Weapon_Equip( pWeapon ); |
|
|
|
int iExtraAmmo = pWeapon->GetExtraAmmoCount(); |
|
|
|
if( iExtraAmmo && !bPickupGrenade ) |
|
{ |
|
//Find out the index of the ammo |
|
int iAmmoIndex = pWeapon->GetPrimaryAmmoType(); |
|
|
|
if( iAmmoIndex != -1 ) |
|
{ |
|
//Remove the extra ammo from the weapon |
|
pWeapon->SetExtraAmmoCount(0); |
|
|
|
//Give it to the player |
|
SetAmmoCount( iExtraAmmo, iAmmoIndex ); |
|
} |
|
} |
|
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "item_pickup" ); |
|
if( event ) |
|
{ |
|
const char *weaponName = pWeapon->GetClassname(); |
|
if ( strncmp( weaponName, "weapon_", 7 ) == 0 ) |
|
{ |
|
weaponName += 7; |
|
} |
|
event->SetInt( "userid", GetUserID() ); |
|
event->SetString( "item", weaponName ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
void CCSPlayer::ResetStamina( void ) |
|
{ |
|
m_flStamina = 0.0f; |
|
} |
|
|
|
void CCSPlayer::RescueZoneTouch( inputdata_t &inputdata ) |
|
{ |
|
m_bInHostageRescueZone = true; |
|
if ( GetTeamNumber() == TEAM_CT && !(m_iDisplayHistoryBits & DHF_IN_RESCUE_ZONE) ) |
|
{ |
|
HintMessage( "#Hint_hostage_rescue_zone", false ); |
|
m_iDisplayHistoryBits |= DHF_IN_RESCUE_ZONE; |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------------------ |
|
CON_COMMAND( timeleft, "prints the time remaining in the match" ) |
|
{ |
|
CCSPlayer *pPlayer = ToCSPlayer( UTIL_GetCommandClient() ); |
|
if ( pPlayer && pPlayer->m_iNextTimeCheck >= gpGlobals->curtime ) |
|
{ |
|
return; // rate limiting |
|
} |
|
|
|
int iTimeRemaining = (int)CSGameRules()->GetMapRemainingTime(); |
|
|
|
if ( iTimeRemaining < 0 ) |
|
{ |
|
if ( pPlayer ) |
|
{ |
|
ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_no_timelimit" ); |
|
} |
|
else |
|
{ |
|
Msg( "* No Time Limit *\n" ); |
|
} |
|
} |
|
else if ( iTimeRemaining == 0 ) |
|
{ |
|
if ( pPlayer ) |
|
{ |
|
ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_last_round" ); |
|
} |
|
else |
|
{ |
|
Msg( "* Last Round *\n" ); |
|
} |
|
} |
|
else |
|
{ |
|
int iMinutes, iSeconds; |
|
iMinutes = iTimeRemaining / 60; |
|
iSeconds = iTimeRemaining % 60; |
|
|
|
char minutes[8]; |
|
char seconds[8]; |
|
|
|
Q_snprintf( minutes, sizeof(minutes), "%d", iMinutes ); |
|
Q_snprintf( seconds, sizeof(seconds), "%2.2d", iSeconds ); |
|
|
|
if ( pPlayer ) |
|
{ |
|
ClientPrint( pPlayer, HUD_PRINTTALK, "#Game_timelimit", minutes, seconds ); |
|
} |
|
else |
|
{ |
|
Msg( "Time Remaining: %s:%s\n", minutes, seconds ); |
|
} |
|
} |
|
|
|
if ( pPlayer ) |
|
{ |
|
pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1; |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------------------------------ |
|
/** |
|
* Emit given sound that only we can hear |
|
*/ |
|
void CCSPlayer::EmitPrivateSound( const char *soundName ) |
|
{ |
|
CSoundParameters params; |
|
if (!GetParametersForSound( soundName, params, NULL )) |
|
return; |
|
|
|
CSingleUserRecipientFilter filter( this ); |
|
EmitSound( filter, entindex(), soundName ); |
|
} |
|
|
|
|
|
//===================== |
|
//Autobuy |
|
//===================== |
|
static void AutoBuy( void ) |
|
{ |
|
CCSPlayer *player = ToCSPlayer( UTIL_GetCommandClient() ); |
|
|
|
if ( player ) |
|
player->AutoBuy(); |
|
} |
|
static ConCommand autobuy( "autobuy", AutoBuy, "Attempt to purchase items with the order listed in cl_autobuy" ); |
|
|
|
//============================================== |
|
//AutoBuy - do the work of deciding what to buy |
|
//============================================== |
|
void CCSPlayer::AutoBuy() |
|
{ |
|
if ( !IsInBuyZone() ) |
|
{ |
|
EmitPrivateSound( "BuyPreset.CantBuy" ); |
|
return; |
|
} |
|
|
|
const char *autobuyString = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_autobuy" ); |
|
if ( !autobuyString || !*autobuyString ) |
|
{ |
|
EmitPrivateSound( "BuyPreset.AlreadyBought" ); |
|
return; |
|
} |
|
|
|
bool boughtPrimary = false, boughtSecondary = false; |
|
|
|
m_bIsInAutoBuy = true; |
|
ParseAutoBuyString(autobuyString, boughtPrimary, boughtSecondary); |
|
m_bIsInAutoBuy = false; |
|
|
|
m_bAutoReload = true; |
|
|
|
//TODO ?: stripped out all the attempts to buy career weapons. |
|
// as we're not porting cs:cz, these were skipped |
|
} |
|
|
|
void CCSPlayer::ParseAutoBuyString(const char *string, bool &boughtPrimary, bool &boughtSecondary) |
|
{ |
|
char command[32]; |
|
int nBuffSize = sizeof(command) - 1; // -1 to leave space for the NULL at the end of the string |
|
const char *c = string; |
|
|
|
if (c == NULL) |
|
{ |
|
EmitPrivateSound( "BuyPreset.AlreadyBought" ); |
|
return; |
|
} |
|
|
|
BuyResult_e overallResult = BUY_ALREADY_HAVE; |
|
|
|
// loop through the string of commands, trying each one in turn. |
|
while (*c != 0) |
|
{ |
|
int i = 0; |
|
// copy the next word into the command buffer. |
|
while ((*c != 0) && (*c != ' ') && (i < nBuffSize)) |
|
{ |
|
command[i] = *(c); |
|
++c; |
|
++i; |
|
} |
|
if (*c == ' ') |
|
{ |
|
++c; // skip the space. |
|
} |
|
|
|
command[i] = 0; // terminate the string. |
|
|
|
// clear out any spaces. |
|
i = 0; |
|
while (command[i] != 0) |
|
{ |
|
if (command[i] == ' ') |
|
{ |
|
command[i] = 0; |
|
break; |
|
} |
|
++i; |
|
} |
|
|
|
// make sure we actually have a command. |
|
if (strlen(command) == 0) |
|
{ |
|
continue; |
|
} |
|
|
|
AutoBuyInfoStruct * commandInfo = GetAutoBuyCommandInfo(command); |
|
|
|
if (ShouldExecuteAutoBuyCommand(commandInfo, boughtPrimary, boughtSecondary)) |
|
{ |
|
BuyResult_e result = HandleCommand_Buy( command ); |
|
|
|
overallResult = CombineBuyResults( overallResult, result ); |
|
|
|
// check to see if we actually bought a primary or secondary weapon this time. |
|
PostAutoBuyCommandProcessing(commandInfo, boughtPrimary, boughtSecondary); |
|
} |
|
} |
|
|
|
if ( overallResult == BUY_CANT_AFFORD ) |
|
{ |
|
EmitPrivateSound( "BuyPreset.CantBuy" ); |
|
} |
|
else if ( overallResult == BUY_ALREADY_HAVE ) |
|
{ |
|
EmitPrivateSound( "BuyPreset.AlreadyBought" ); |
|
} |
|
else if ( overallResult == BUY_BOUGHT ) |
|
{ |
|
g_iAutoBuyPurchases++; |
|
} |
|
} |
|
|
|
BuyResult_e CCSPlayer::CombineBuyResults( BuyResult_e prevResult, BuyResult_e newResult ) |
|
{ |
|
if ( newResult == BUY_BOUGHT ) |
|
{ |
|
prevResult = BUY_BOUGHT; |
|
} |
|
else if ( prevResult != BUY_BOUGHT && |
|
(newResult == BUY_CANT_AFFORD || newResult == BUY_INVALID_ITEM || newResult == BUY_PLAYER_CANT_BUY ) ) |
|
{ |
|
prevResult = BUY_CANT_AFFORD; |
|
} |
|
|
|
return prevResult; |
|
} |
|
|
|
//============================================== |
|
//PostAutoBuyCommandProcessing |
|
//============================================== |
|
void CCSPlayer::PostAutoBuyCommandProcessing(const AutoBuyInfoStruct *commandInfo, bool &boughtPrimary, bool &boughtSecondary) |
|
{ |
|
if (commandInfo == NULL) |
|
{ |
|
return; |
|
} |
|
|
|
CBaseCombatWeapon *pPrimary = Weapon_GetSlot( WEAPON_SLOT_RIFLE ); |
|
CBaseCombatWeapon *pSecondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
|
|
if ((pPrimary != NULL) && (stricmp(pPrimary->GetClassname(), commandInfo->m_classname) == 0)) |
|
{ |
|
// I just bought the gun I was trying to buy. |
|
boughtPrimary = true; |
|
} |
|
else if ((pPrimary == NULL) && ((commandInfo->m_class & AUTOBUYCLASS_SHIELD) == AUTOBUYCLASS_SHIELD) && HasShield()) |
|
{ |
|
// the shield is a primary weapon even though it isn't a "real" weapon. |
|
boughtPrimary = true; |
|
} |
|
else if ((pSecondary != NULL) && (stricmp(pSecondary->GetClassname(), commandInfo->m_classname) == 0)) |
|
{ |
|
// I just bought the pistol I was trying to buy. |
|
boughtSecondary = true; |
|
} |
|
} |
|
|
|
bool CCSPlayer::ShouldExecuteAutoBuyCommand(const AutoBuyInfoStruct *commandInfo, bool boughtPrimary, bool boughtSecondary) |
|
{ |
|
if (commandInfo == NULL) |
|
{ |
|
return false; |
|
} |
|
|
|
if ((boughtPrimary) && ((commandInfo->m_class & AUTOBUYCLASS_PRIMARY) != 0) && ((commandInfo->m_class & AUTOBUYCLASS_AMMO) == 0)) |
|
{ |
|
// this is a primary weapon and we already have one. |
|
return false; |
|
} |
|
|
|
if ((boughtSecondary) && ((commandInfo->m_class & AUTOBUYCLASS_SECONDARY) != 0) && ((commandInfo->m_class & AUTOBUYCLASS_AMMO) == 0)) |
|
{ |
|
// this is a secondary weapon and we already have one. |
|
return false; |
|
} |
|
|
|
if( commandInfo->m_class & AUTOBUYCLASS_ARMOR && ArmorValue() >= 100 ) |
|
{ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
AutoBuyInfoStruct *CCSPlayer::GetAutoBuyCommandInfo(const char *command) |
|
{ |
|
int i = 0; |
|
AutoBuyInfoStruct *ret = NULL; |
|
AutoBuyInfoStruct *temp = &(g_autoBuyInfo[i]); |
|
|
|
// loop through all the commands till we find the one that matches. |
|
while ((ret == NULL) && (temp->m_class != (AutoBuyClassType)0)) |
|
{ |
|
temp = &(g_autoBuyInfo[i]); |
|
++i; |
|
|
|
if (stricmp(temp->m_command, command) == 0) |
|
{ |
|
ret = temp; |
|
} |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
//============================================== |
|
//PostAutoBuyCommandProcessing |
|
//- reorders the tokens in autobuyString based on the order of tokens in the priorityString. |
|
//============================================== |
|
void CCSPlayer::PrioritizeAutoBuyString(char *autobuyString, const char *priorityString) |
|
{ |
|
char newString[256]; |
|
int newStringPos = 0; |
|
char priorityToken[32]; |
|
|
|
if ((priorityString == NULL) || (autobuyString == NULL)) |
|
{ |
|
return; |
|
} |
|
|
|
const char *priorityChar = priorityString; |
|
|
|
while (*priorityChar != 0) |
|
{ |
|
int i = 0; |
|
|
|
// get the next token from the priority string. |
|
while ((*priorityChar != 0) && (*priorityChar != ' ')) |
|
{ |
|
priorityToken[i] = *priorityChar; |
|
++i; |
|
++priorityChar; |
|
} |
|
priorityToken[i] = 0; |
|
|
|
// skip spaces |
|
while (*priorityChar == ' ') |
|
{ |
|
++priorityChar; |
|
} |
|
|
|
if (strlen(priorityToken) == 0) |
|
{ |
|
continue; |
|
} |
|
|
|
// see if the priority token is in the autobuy string. |
|
// if it is, copy that token to the new string and blank out |
|
// that token in the autobuy string. |
|
char *autoBuyPosition = strstr(autobuyString, priorityToken); |
|
if (autoBuyPosition != NULL) |
|
{ |
|
while ((*autoBuyPosition != 0) && (*autoBuyPosition != ' ')) |
|
{ |
|
newString[newStringPos] = *autoBuyPosition; |
|
*autoBuyPosition = ' '; |
|
++newStringPos; |
|
++autoBuyPosition; |
|
} |
|
|
|
newString[newStringPos++] = ' '; |
|
} |
|
} |
|
|
|
// now just copy anything left in the autobuyString to the new string in the order it's in already. |
|
char *autobuyPosition = autobuyString; |
|
while (*autobuyPosition != 0) |
|
{ |
|
// skip spaces |
|
while (*autobuyPosition == ' ') |
|
{ |
|
++autobuyPosition; |
|
} |
|
|
|
// copy the token over to the new string. |
|
while ((*autobuyPosition != 0) && (*autobuyPosition != ' ')) |
|
{ |
|
newString[newStringPos] = *autobuyPosition; |
|
++newStringPos; |
|
++autobuyPosition; |
|
} |
|
|
|
// add a space at the end. |
|
newString[newStringPos++] = ' '; |
|
} |
|
|
|
// terminate the string. Trailing spaces shouldn't matter. |
|
newString[newStringPos] = 0; |
|
|
|
Q_snprintf(autobuyString, sizeof(autobuyString), "%s", newString); |
|
} |
|
|
|
|
|
//============================================================== |
|
// ReBuy |
|
// system for attempting to buy the weapons you had last round |
|
//============================================================== |
|
static void Rebuy( void ) |
|
{ |
|
CCSPlayer *player = ToCSPlayer( UTIL_GetCommandClient() ); |
|
|
|
if ( player ) |
|
player->Rebuy(); |
|
} |
|
static ConCommand rebuy( "rebuy", Rebuy, "Attempt to repurchase items with the order listed in cl_rebuy" ); |
|
|
|
void CCSPlayer::BuildRebuyStruct() |
|
{ |
|
if (m_bIsInRebuy) |
|
{ |
|
// if we are in the middle of a rebuy, we don't want to update the buy struct. |
|
return; |
|
} |
|
|
|
CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE ); |
|
CBaseCombatWeapon *secondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
|
|
// do the primary weapon/ammo stuff. |
|
if (primary == NULL) |
|
{ |
|
// count a shieldgun as a primary. |
|
if (HasShield()) |
|
{ |
|
//m_rebuyStruct.m_primaryWeapon = WEAPON_SHIELDGUN; |
|
Q_strncpy( m_rebuyStruct.m_szPrimaryWeapon, "shield", sizeof(m_rebuyStruct.m_szPrimaryWeapon) ); |
|
m_rebuyStruct.m_primaryAmmo = 0; // shields don't have ammo. |
|
} |
|
else |
|
{ |
|
|
|
m_rebuyStruct.m_szPrimaryWeapon[0] = 0; // if we don't have a shield and we don't have a primary weapon, we got nuthin. |
|
m_rebuyStruct.m_primaryAmmo = 0; // can't have ammo if we don't have a gun right? |
|
} |
|
} |
|
else |
|
{ |
|
//strip off the "weapon_" |
|
|
|
const char *wpnName = primary->GetClassname(); |
|
|
|
Q_strncpy( m_rebuyStruct.m_szPrimaryWeapon, wpnName + 7, sizeof(m_rebuyStruct.m_szPrimaryWeapon) ); |
|
|
|
if( primary->GetPrimaryAmmoType() != -1 ) |
|
{ |
|
m_rebuyStruct.m_primaryAmmo = GetAmmoCount( primary->GetPrimaryAmmoType() ); |
|
} |
|
} |
|
|
|
// do the secondary weapon/ammo stuff. |
|
if (secondary == NULL) |
|
{ |
|
m_rebuyStruct.m_szSecondaryWeapon[0] = 0; |
|
m_rebuyStruct.m_secondaryAmmo = 0; // can't have ammo if we don't have a gun right? |
|
} |
|
else |
|
{ |
|
const char *wpnName = secondary->GetClassname(); |
|
|
|
Q_strncpy( m_rebuyStruct.m_szSecondaryWeapon, wpnName + 7, sizeof(m_rebuyStruct.m_szSecondaryWeapon) ); |
|
|
|
if( secondary->GetPrimaryAmmoType() != -1 ) |
|
{ |
|
m_rebuyStruct.m_secondaryAmmo = GetAmmoCount( secondary->GetPrimaryAmmoType() ); |
|
} |
|
} |
|
|
|
CBaseCombatWeapon *pGrenade; |
|
|
|
//MATTTODO: right now you can't buy more than one grenade. make it so you can |
|
//buy more and query the number you have. |
|
// HE Grenade |
|
pGrenade = Weapon_OwnsThisType( "weapon_hegrenade" ); |
|
if ( pGrenade && pGrenade->GetPrimaryAmmoType() != -1 ) |
|
{ |
|
m_rebuyStruct.m_heGrenade = GetAmmoCount(pGrenade->GetPrimaryAmmoType()); |
|
} |
|
else |
|
m_rebuyStruct.m_heGrenade = 0; |
|
|
|
|
|
// flashbang |
|
pGrenade = Weapon_OwnsThisType( "weapon_flashbang" ); |
|
if ( pGrenade && pGrenade->GetPrimaryAmmoType() != -1 ) |
|
{ |
|
m_rebuyStruct.m_flashbang = GetAmmoCount(pGrenade->GetPrimaryAmmoType()); |
|
} |
|
else |
|
m_rebuyStruct.m_flashbang = 0; |
|
|
|
// smokegrenade |
|
pGrenade = Weapon_OwnsThisType( "weapon_smokegrenade" ); |
|
if ( pGrenade /*&& pGrenade->GetPrimaryAmmoType() != -1*/ ) |
|
{ |
|
m_rebuyStruct.m_smokeGrenade = 1; //GetAmmoCount(pGrenade->GetPrimaryAmmoType()); |
|
} |
|
else |
|
m_rebuyStruct.m_smokeGrenade = 0; |
|
|
|
// defuser |
|
m_rebuyStruct.m_defuser = HasDefuser(); |
|
|
|
// night vision |
|
m_rebuyStruct.m_nightVision = m_bHasNightVision.Get(); //cast to avoid strange compiler warning |
|
|
|
// check for armor. |
|
m_rebuyStruct.m_armor = ( m_bHasHelmet ? 2 : ( ArmorValue() > 0 ? 1 : 0 ) ); |
|
} |
|
|
|
void CCSPlayer::Rebuy( void ) |
|
{ |
|
if ( !IsInBuyZone() ) |
|
{ |
|
EmitPrivateSound( "BuyPreset.CantBuy" ); |
|
return; |
|
} |
|
|
|
const char *rebuyString = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_rebuy" ); |
|
if ( !rebuyString || !*rebuyString ) |
|
{ |
|
EmitPrivateSound( "BuyPreset.AlreadyBought" ); |
|
return; |
|
} |
|
|
|
m_bIsInRebuy = true; |
|
BuyResult_e overallResult = BUY_ALREADY_HAVE; |
|
|
|
char token[256]; |
|
rebuyString = engine->ParseFile( rebuyString, token, sizeof( token ) ); |
|
|
|
while (rebuyString != NULL) |
|
{ |
|
BuyResult_e result = BUY_ALREADY_HAVE; |
|
|
|
if (!Q_strncmp(token, "PrimaryWeapon", 14)) |
|
{ |
|
result = RebuyPrimaryWeapon(); |
|
} |
|
else if (!Q_strncmp(token, "PrimaryAmmo", 12)) |
|
{ |
|
result = RebuyPrimaryAmmo(); |
|
} |
|
else if (!Q_strncmp(token, "SecondaryWeapon", 16)) |
|
{ |
|
result = RebuySecondaryWeapon(); |
|
} |
|
else if (!Q_strncmp(token, "SecondaryAmmo", 14)) |
|
{ |
|
result = RebuySecondaryAmmo(); |
|
} |
|
else if (!Q_strncmp(token, "HEGrenade", 10)) |
|
{ |
|
result = RebuyHEGrenade(); |
|
} |
|
else if (!Q_strncmp(token, "Flashbang", 10)) |
|
{ |
|
result = RebuyFlashbang(); |
|
} |
|
else if (!Q_strncmp(token, "SmokeGrenade", 13)) |
|
{ |
|
result = RebuySmokeGrenade(); |
|
} |
|
else if (!Q_strncmp(token, "Defuser", 8)) |
|
{ |
|
result = RebuyDefuser(); |
|
} |
|
else if (!Q_strncmp(token, "NightVision", 12)) |
|
{ |
|
result = RebuyNightVision(); |
|
} |
|
else if (!Q_strncmp(token, "Armor", 6)) |
|
{ |
|
result = RebuyArmor(); |
|
} |
|
|
|
overallResult = CombineBuyResults( overallResult, result ); |
|
|
|
rebuyString = engine->ParseFile( rebuyString, token, sizeof( token ) ); |
|
} |
|
|
|
m_bIsInRebuy = false; |
|
|
|
// after we're done buying, the user is done with their equipment purchasing experience. |
|
// so we are effectively out of the buy zone. |
|
// if (TheTutor != NULL) |
|
// { |
|
// TheTutor->OnEvent(EVENT_PLAYER_LEFT_BUY_ZONE); |
|
// } |
|
|
|
m_bAutoReload = true; |
|
|
|
if ( overallResult == BUY_CANT_AFFORD ) |
|
{ |
|
EmitPrivateSound( "BuyPreset.CantBuy" ); |
|
} |
|
else if ( overallResult == BUY_ALREADY_HAVE ) |
|
{ |
|
EmitPrivateSound( "BuyPreset.AlreadyBought" ); |
|
} |
|
else if ( overallResult == BUY_BOUGHT ) |
|
{ |
|
g_iReBuyPurchases++; |
|
} |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuyPrimaryWeapon() |
|
{ |
|
CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE ); |
|
if (primary != NULL) |
|
{ |
|
return BUY_ALREADY_HAVE; // don't drop primary weapons via rebuy - if the player picked up a different weapon, he wants to keep it. |
|
} |
|
|
|
if( strlen( m_rebuyStruct.m_szPrimaryWeapon ) > 0 ) |
|
return HandleCommand_Buy(m_rebuyStruct.m_szPrimaryWeapon); |
|
|
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuySecondaryWeapon() |
|
{ |
|
CBaseCombatWeapon *pistol = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
if (pistol != NULL && !m_bUsingDefaultPistol) |
|
{ |
|
return BUY_ALREADY_HAVE; // don't drop pistols via rebuy if we've bought one other than the default pistol |
|
} |
|
|
|
if( strlen( m_rebuyStruct.m_szSecondaryWeapon ) > 0 ) |
|
return HandleCommand_Buy(m_rebuyStruct.m_szSecondaryWeapon); |
|
|
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuyPrimaryAmmo() |
|
{ |
|
CBaseCombatWeapon *primary = Weapon_GetSlot( WEAPON_SLOT_RIFLE ); |
|
|
|
if (primary == NULL) |
|
{ |
|
return BUY_ALREADY_HAVE; // can't buy ammo when we don't even have a gun. |
|
} |
|
|
|
// Ensure that the weapon uses ammo |
|
int nAmmo = primary->GetPrimaryAmmoType(); |
|
if ( nAmmo == -1 ) |
|
{ |
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
// if we had more ammo before than we have now, buy more. |
|
if (m_rebuyStruct.m_primaryAmmo > GetAmmoCount( nAmmo )) |
|
{ |
|
return HandleCommand_Buy("primammo"); |
|
} |
|
|
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
|
|
BuyResult_e CCSPlayer::RebuySecondaryAmmo() |
|
{ |
|
CBaseCombatWeapon *secondary = Weapon_GetSlot( WEAPON_SLOT_PISTOL ); |
|
|
|
if (secondary == NULL) |
|
{ |
|
return BUY_ALREADY_HAVE; // can't buy ammo when we don't even have a gun. |
|
} |
|
|
|
// Ensure that the weapon uses ammo |
|
int nAmmo = secondary->GetPrimaryAmmoType(); |
|
if ( nAmmo == -1 ) |
|
{ |
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
if (m_rebuyStruct.m_secondaryAmmo > GetAmmoCount( nAmmo )) |
|
{ |
|
return HandleCommand_Buy("secammo"); |
|
} |
|
|
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuyHEGrenade() |
|
{ |
|
CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_hegrenade" ); |
|
|
|
int numGrenades = 0; |
|
|
|
if( pGrenade ) |
|
{ |
|
int nAmmo = pGrenade->GetPrimaryAmmoType(); |
|
if ( nAmmo == -1 ) |
|
{ |
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
numGrenades = GetAmmoCount( nAmmo ); |
|
} |
|
|
|
BuyResult_e overallResult = BUY_ALREADY_HAVE; |
|
int numToBuy = MAX( 0, m_rebuyStruct.m_heGrenade - numGrenades ); |
|
for (int i = 0; i < numToBuy; ++i) |
|
{ |
|
BuyResult_e result = HandleCommand_Buy("hegrenade"); |
|
overallResult = CombineBuyResults( overallResult, result ); |
|
} |
|
|
|
return overallResult; |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuyFlashbang() |
|
{ |
|
CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_flashbang" ); |
|
|
|
int numGrenades = 0; |
|
|
|
if( pGrenade ) |
|
{ |
|
int nAmmo = pGrenade->GetPrimaryAmmoType(); |
|
if ( nAmmo == -1 ) |
|
{ |
|
return BUY_ALREADY_HAVE; |
|
} |
|
numGrenades = GetAmmoCount( nAmmo ); |
|
|
|
} |
|
|
|
BuyResult_e overallResult = BUY_ALREADY_HAVE; |
|
int numToBuy = MAX( 0, m_rebuyStruct.m_flashbang - numGrenades ); |
|
for (int i = 0; i < numToBuy; ++i) |
|
{ |
|
BuyResult_e result = HandleCommand_Buy("flashbang"); |
|
overallResult = CombineBuyResults( overallResult, result ); |
|
} |
|
|
|
return overallResult; |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuySmokeGrenade() |
|
{ |
|
CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType( "weapon_smokegrenade" ); |
|
|
|
int numGrenades = 0; |
|
|
|
if( pGrenade ) |
|
{ |
|
int nAmmo = pGrenade->GetPrimaryAmmoType(); |
|
if ( nAmmo == -1 ) |
|
{ |
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
numGrenades = GetAmmoCount( nAmmo ); |
|
} |
|
|
|
BuyResult_e overallResult = BUY_ALREADY_HAVE; |
|
int numToBuy = MAX( 0, m_rebuyStruct.m_smokeGrenade - numGrenades ); |
|
for (int i = 0; i < numToBuy; ++i) |
|
{ |
|
BuyResult_e result = HandleCommand_Buy("smokegrenade"); |
|
overallResult = CombineBuyResults( overallResult, result ); |
|
} |
|
|
|
return overallResult; |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuyDefuser() |
|
{ |
|
//If we don't have a defuser, and we want one, buy it! |
|
if( !HasDefuser() && m_rebuyStruct.m_defuser ) |
|
{ |
|
return HandleCommand_Buy("defuser"); |
|
} |
|
|
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuyNightVision() |
|
{ |
|
//if we don't have night vision and we want one, buy it! |
|
if( !m_bHasNightVision && m_rebuyStruct.m_nightVision ) |
|
{ |
|
return HandleCommand_Buy("nvgs"); |
|
} |
|
|
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
BuyResult_e CCSPlayer::RebuyArmor() |
|
{ |
|
if (m_rebuyStruct.m_armor > 0 ) |
|
{ |
|
int armor = 0; |
|
|
|
if( m_bHasHelmet ) |
|
armor = 2; |
|
else if( ArmorValue() > 0 ) |
|
armor = 1; |
|
|
|
if( armor < m_rebuyStruct.m_armor ) |
|
{ |
|
if (m_rebuyStruct.m_armor == 1) |
|
{ |
|
return HandleCommand_Buy("vest"); |
|
} |
|
else |
|
{ |
|
return HandleCommand_Buy("vesthelm"); |
|
} |
|
} |
|
|
|
} |
|
|
|
return BUY_ALREADY_HAVE; |
|
} |
|
|
|
bool CCSPlayer::IsUseableEntity( CBaseEntity *pEntity, unsigned int requiredCaps ) |
|
{ |
|
CWeaponCSBase *pCSWepaon = dynamic_cast<CWeaponCSBase*>(pEntity); |
|
|
|
if( pCSWepaon ) |
|
{ |
|
// we can't USE dropped weapons |
|
return true; |
|
} |
|
|
|
CBaseCSGrenadeProjectile *pGrenade = dynamic_cast<CBaseCSGrenadeProjectile*>(pEntity); |
|
if ( pGrenade ) |
|
{ |
|
// we can't USE thrown grenades |
|
} |
|
|
|
return BaseClass::IsUseableEntity( pEntity, requiredCaps ); |
|
} |
|
|
|
CBaseEntity *CCSPlayer::FindUseEntity() |
|
{ |
|
CBaseEntity *entity = NULL; |
|
|
|
// Check to see if the bomb is close enough to use before attempting to use anything else. |
|
|
|
if ( CSGameRules()->IsBombDefuseMap() && GetTeamNumber() == TEAM_CT ) |
|
{ |
|
// This is done separately since there might be something blocking our LOS to it |
|
// but we might want to use it anyway if it's close enough. This should eliminate |
|
// the vast majority of bomb placement exploits (places where the bomb can be planted |
|
// but can't be "used". This also mimics goldsrc cstrike behavior. |
|
CBaseEntity *bomb = gEntList.FindEntityByClassname( NULL, PLANTED_C4_CLASSNAME ); |
|
if (bomb != NULL) |
|
{ |
|
Vector bombPos = bomb->GetAbsOrigin(); |
|
Vector vecLOS = EyePosition() - bombPos; |
|
|
|
if (vecLOS.LengthSqr() < (96*96)) // 64 is the distance in Goldsrc. However since Goldsrc did distance from the player's origin and we're doing distance from the player's eye, make the radius a bit bigger. |
|
{ |
|
// bomb is close enough, now make sure the player is facing the bomb. |
|
Vector forward; |
|
AngleVectors(EyeAngles(), &forward, NULL, NULL); |
|
|
|
vecLOS.NormalizeInPlace(); |
|
|
|
float flDot = DotProduct(forward, vecLOS); |
|
if (flDot < -0.7) // 0.7 taken from Goldsrc, +/- ~45 degrees |
|
{ |
|
entity = bomb; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ( entity == NULL ) |
|
{ |
|
entity = BaseClass::FindUseEntity(); |
|
} |
|
|
|
return entity; |
|
} |
|
|
|
void CCSPlayer::StockPlayerAmmo( CBaseCombatWeapon *pNewWeapon ) |
|
{ |
|
CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase * >( pNewWeapon ); |
|
|
|
if ( pWeapon ) |
|
{ |
|
if ( pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE ) |
|
return; |
|
|
|
int nAmmo = pWeapon->GetPrimaryAmmoType(); |
|
|
|
if ( nAmmo != -1 ) |
|
{ |
|
GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName ); |
|
pWeapon->m_iClip1 = pWeapon->GetMaxClip1(); |
|
} |
|
|
|
return; |
|
} |
|
|
|
pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE )); |
|
|
|
if ( pWeapon ) |
|
{ |
|
int nAmmo = pWeapon->GetPrimaryAmmoType(); |
|
|
|
if ( nAmmo != -1 ) |
|
{ |
|
GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName ); |
|
pWeapon->m_iClip1 = pWeapon->GetMaxClip1(); |
|
} |
|
} |
|
|
|
pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL )); |
|
|
|
if ( pWeapon ) |
|
{ |
|
int nAmmo = pWeapon->GetPrimaryAmmoType(); |
|
|
|
if ( nAmmo != -1 ) |
|
{ |
|
GiveAmmo( 9999, GetAmmoDef()->GetAmmoOfIndex(nAmmo)->pName ); |
|
pWeapon->m_iClip1 = pWeapon->GetMaxClip1(); |
|
} |
|
} |
|
} |
|
|
|
CBaseEntity *CCSPlayer::GiveNamedItem( const char *pszName, int iSubType ) |
|
{ |
|
EHANDLE pent; |
|
|
|
if ( !pszName || !pszName[0] ) |
|
return NULL; |
|
|
|
#ifndef CS_SHIELD_ENABLED |
|
if ( !Q_stricmp( pszName, "weapon_shield" ) ) |
|
return NULL; |
|
#endif |
|
|
|
pent = CreateEntityByName(pszName); |
|
if ( pent == NULL ) |
|
{ |
|
Msg( "NULL Ent in GiveNamedItem!\n" ); |
|
return NULL; |
|
} |
|
|
|
pent->SetLocalOrigin( GetLocalOrigin() ); |
|
pent->AddSpawnFlags( SF_NORESPAWN ); |
|
|
|
CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>( (CBaseEntity*)pent ); |
|
if ( pWeapon ) |
|
{ |
|
if ( iSubType ) |
|
{ |
|
pWeapon->SetSubType( iSubType ); |
|
} |
|
} |
|
|
|
DispatchSpawn( pent ); |
|
|
|
m_bIsBeingGivenItem = true; |
|
if ( pent != NULL && !(pent->IsMarkedForDeletion()) ) |
|
{ |
|
pent->Touch( this ); |
|
} |
|
m_bIsBeingGivenItem = false; |
|
|
|
StockPlayerAmmo( pWeapon ); |
|
return pent; |
|
} |
|
|
|
|
|
void CCSPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) |
|
{ |
|
if ( event == PLAYERANIMEVENT_THROW_GRENADE ) |
|
{ |
|
// Grenade throwing has to synchronize exactly with the player's grenade weapon going away, |
|
// and events get delayed a bit, so we let CCSPlayerAnimState pickup the change to this |
|
// variable. |
|
m_iThrowGrenadeCounter = (m_iThrowGrenadeCounter+1) % (1<<THROWGRENADE_COUNTER_BITS); |
|
} |
|
else |
|
{ |
|
m_PlayerAnimState->DoAnimationEvent( event, nData ); |
|
TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy. |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
int CCSPlayer::FlashlightIsOn( void ) |
|
{ |
|
return IsEffectActive( EF_DIMLIGHT ); |
|
} |
|
|
|
extern ConVar flashlight; |
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::FlashlightTurnOn( void ) |
|
{ |
|
if( flashlight.GetInt() > 0 && IsAlive() ) |
|
{ |
|
AddEffects( EF_DIMLIGHT ); |
|
EmitSound( "Player.FlashlightOn" ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::FlashlightTurnOff( void ) |
|
{ |
|
RemoveEffects( EF_DIMLIGHT ); |
|
|
|
if( IsAlive() ) |
|
{ |
|
EmitSound( "Player.FlashlightOff" ); |
|
} |
|
} |
|
|
|
|
|
//Drop the appropriate weapons: |
|
// Defuser if we have one |
|
// C4 if we have one |
|
// The best weapon we have, first check primary, |
|
// then secondary and drop the best one |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj] Added a parameter so we know if it was death that caused the drop |
|
// [menglish] Clear all previously dropped equipment and add the c4 to the dropped equipment |
|
//============================================================================= |
|
|
|
void CCSPlayer::DropWeapons( bool fromDeath, bool friendlyFire ) |
|
{ |
|
for ( int i = 0; i < DROPPED_COUNT; ++i ) |
|
{ |
|
m_hDroppedEquipment[i] = NULL; |
|
} |
|
|
|
CBaseCombatWeapon *pC4 = Weapon_OwnsThisType( "weapon_c4" ); |
|
if ( pC4 ) |
|
{ |
|
CSWeaponDrop( pC4, false, true ); |
|
if( fromDeath ) |
|
{ |
|
if( friendlyFire ) |
|
{ |
|
(static_cast<CC4*> (pC4))->SetDroppedFromDeath(true); |
|
} |
|
m_hDroppedEquipment[DROPPED_C4] = static_cast<CBaseEntity *>(pC4); |
|
} |
|
} |
|
|
|
//NOTE: Function continues beyond comment block. This is just the part I touched. |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
|
|
if( HasDefuser() ) |
|
{ |
|
//Drop an item_defuser |
|
Vector vForward, vRight; |
|
AngleVectors( GetAbsAngles(), &vForward, &vRight, NULL ); |
|
|
|
CBaseAnimating *pDefuser = (CBaseAnimating *)CBaseEntity::Create( "item_defuser", WorldSpaceCenter(), GetLocalAngles(), this ); |
|
pDefuser->ApplyAbsVelocityImpulse( vForward * 200 + vRight * random->RandomFloat( -50, 50 ) ); |
|
|
|
RemoveDefuser(); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Add the newly created defuser to the dropped equipment list |
|
//============================================================================= |
|
|
|
if(fromDeath) |
|
{ |
|
m_hDroppedEquipment[DROPPED_DEFUSE] = static_cast<CBaseEntity *>(pDefuser); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
} |
|
|
|
if( HasShield() ) |
|
{ |
|
DropShield(); |
|
} |
|
else |
|
{ |
|
//drop the best weapon we have |
|
if( !DropRifle( true ) ) |
|
DropPistol( true ); |
|
} |
|
|
|
// drop any live grenades so they explode |
|
CBaseCSGrenade *pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_hegrenade")); |
|
if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) ) |
|
{ |
|
pGrenade->DropGrenade(); |
|
pGrenade->DecrementAmmo( this ); |
|
} |
|
else |
|
{ |
|
pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_flashbang")); |
|
if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) ) |
|
{ |
|
pGrenade->DropGrenade(); |
|
pGrenade->DecrementAmmo( this ); |
|
} |
|
else |
|
{ |
|
pGrenade = dynamic_cast< CBaseCSGrenade * >(Weapon_OwnsThisType("weapon_smokegrenade")); |
|
if ( pGrenade && ( pGrenade->IsPinPulled() || pGrenade->IsBeingThrown() ) ) |
|
{ |
|
pGrenade->DropGrenade(); |
|
pGrenade->DecrementAmmo( this ); |
|
} |
|
} |
|
} |
|
|
|
// drop the "best" grenade remaining |
|
CBaseCombatWeapon *pWeapon = Weapon_OwnsThisType("weapon_hegrenade"); |
|
bool grenadeDrop = false; |
|
if ( pWeapon && pWeapon->HasAmmo() ) |
|
{ |
|
grenadeDrop = CSWeaponDrop(pWeapon, false); |
|
} |
|
else |
|
{ |
|
pWeapon = Weapon_OwnsThisType("weapon_flashbang"); |
|
if ( pWeapon && pWeapon->HasAmmo() ) |
|
{ |
|
grenadeDrop = CSWeaponDrop(pWeapon, false); |
|
} |
|
else |
|
{ |
|
pWeapon = Weapon_OwnsThisType("weapon_smokegrenade"); |
|
if ( pWeapon && pWeapon->HasAmmo() ) |
|
{ |
|
grenadeDrop = CSWeaponDrop(pWeapon, false); |
|
} |
|
} |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] Add whichever, if any, grenade was dropped |
|
//============================================================================= |
|
|
|
if( pWeapon && grenadeDrop ) |
|
{ |
|
m_hDroppedEquipment[DROPPED_GRENADE] = static_cast<CBaseEntity *>(pWeapon); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Put the player in the specified team |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::ChangeTeam( int iTeamNum ) |
|
{ |
|
if ( !GetGlobalTeam( iTeamNum ) ) |
|
{ |
|
Warning( "CCSPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum ); |
|
return; |
|
} |
|
|
|
int iOldTeam = GetTeamNumber(); |
|
|
|
// if this is our current team, just abort |
|
if ( iTeamNum == iOldTeam ) |
|
return; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
//============================================================================= |
|
|
|
// [tj] Added a parameter so we know if it was death that caused the drop |
|
// Drop Our best weapon |
|
DropWeapons(false, false); |
|
|
|
// [tj] Clear out dominations |
|
RemoveNemesisRelationships(); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
|
|
// Always allow a change to spectator, and don't count it as one of our team changes. |
|
// We now store the old team, so if a player changes once to one team, then to spectator, |
|
// they won't be able to change back to their old old team, but will still be able to join |
|
// the team they initially changed to. |
|
if( iTeamNum != TEAM_SPECTATOR ) |
|
{ |
|
m_bTeamChanged = true; |
|
} |
|
else |
|
{ |
|
m_iOldTeam = iOldTeam; |
|
} |
|
|
|
// do the team change: |
|
BaseClass::ChangeTeam( iTeamNum ); |
|
|
|
//reset class |
|
m_iClass = (int)CS_CLASS_NONE; |
|
|
|
// update client state |
|
|
|
if ( iTeamNum == TEAM_UNASSIGNED ) |
|
{ |
|
State_Transition( STATE_OBSERVER_MODE ); |
|
} |
|
else if ( iTeamNum == TEAM_SPECTATOR ) |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj] Removed these lines so players keep their money when switching to spectator. |
|
//============================================================================= |
|
//Reset money |
|
//m_iAccount = 0; |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
RemoveAllItems( true ); |
|
|
|
State_Transition( STATE_OBSERVER_MODE ); |
|
} |
|
else // active player |
|
{ |
|
if ( iOldTeam == TEAM_SPECTATOR ) |
|
{ |
|
// If they're switching from being a spectator to ingame player |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [tj] Changed this so players either retain their existing money or, |
|
// if they have less than the default, give them the default. |
|
//============================================================================= |
|
int startMoney = CSGameRules()->GetStartMoney(); |
|
if (startMoney > m_iAccount) |
|
{ |
|
m_iAccount = startMoney; |
|
} |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
} |
|
|
|
// bots get to this state on TEAM_UNASSIGNED, yet they are marked alive. Don't kill them. |
|
else if ( iOldTeam != TEAM_UNASSIGNED && !IsDead() ) |
|
{ |
|
// Kill player if switching teams while alive |
|
CommitSuicide(); |
|
} |
|
|
|
// Put up the class selection menu. |
|
State_Transition( STATE_PICKINGCLASS ); |
|
} |
|
|
|
// Initialize the player counts now that a player has switched teams |
|
int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT; |
|
CSGameRules()->InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Put the player in the specified team without penalty |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::SwitchTeam( int iTeamNum ) |
|
{ |
|
if ( !GetGlobalTeam( iTeamNum ) || (iTeamNum != TEAM_CT && iTeamNum != TEAM_TERRORIST) ) |
|
{ |
|
Warning( "CCSPlayer::SwitchTeam( %d ) - invalid team index.\n", iTeamNum ); |
|
return; |
|
} |
|
|
|
int iOldTeam = GetTeamNumber(); |
|
|
|
// if this is our current team, just abort |
|
if ( iTeamNum == iOldTeam ) |
|
return; |
|
|
|
// Always allow a change to spectator, and don't count it as one of our team changes. |
|
// We now store the old team, so if a player changes once to one team, then to spectator, |
|
// they won't be able to change back to their old old team, but will still be able to join |
|
// the team they initially changed to. |
|
m_bTeamChanged = true; |
|
|
|
// do the team change: |
|
BaseClass::ChangeTeam( iTeamNum ); |
|
|
|
if( HasDefuser() ) |
|
{ |
|
RemoveDefuser(); |
|
} |
|
|
|
//reset class |
|
switch ( m_iClass ) |
|
{ |
|
// Terrorist -> CT |
|
case CS_CLASS_PHOENIX_CONNNECTION: |
|
m_iClass = (int)CS_CLASS_SEAL_TEAM_6; |
|
break; |
|
case CS_CLASS_L337_KREW: |
|
m_iClass = (int)CS_CLASS_GSG_9; |
|
break; |
|
case CS_CLASS_ARCTIC_AVENGERS: |
|
m_iClass = (int)CS_CLASS_SAS; |
|
break; |
|
case CS_CLASS_GUERILLA_WARFARE: |
|
m_iClass = (int)CS_CLASS_GIGN; |
|
break; |
|
|
|
// CT -> Terrorist |
|
case CS_CLASS_SEAL_TEAM_6: |
|
m_iClass = (int)CS_CLASS_PHOENIX_CONNNECTION; |
|
break; |
|
case CS_CLASS_GSG_9: |
|
m_iClass = (int)CS_CLASS_L337_KREW; |
|
break; |
|
case CS_CLASS_SAS: |
|
m_iClass = (int)CS_CLASS_ARCTIC_AVENGERS; |
|
break; |
|
case CS_CLASS_GIGN: |
|
m_iClass = (int)CS_CLASS_GUERILLA_WARFARE; |
|
break; |
|
|
|
case CS_CLASS_NONE: |
|
default: |
|
break; |
|
} |
|
|
|
// Initialize the player counts now that a player has switched teams |
|
int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT; |
|
CSGameRules()->InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT ); |
|
} |
|
|
|
void CCSPlayer::ModifyOrAppendPlayerCriteria( AI_CriteriaSet& set ) |
|
{ |
|
// this is for giving player info to the hostage response system |
|
// and is as yet unused. |
|
// Eventually we could give the hostage a few tidbits about this player, |
|
// eg their health, what weapons they have, and the hostage could |
|
// comment accordingly. |
|
|
|
//do not append any player data to the Criteria! |
|
|
|
//we don't know which player we should be caring about |
|
} |
|
|
|
static unsigned int s_BulletGroupCounter = 0; |
|
|
|
void CCSPlayer::StartNewBulletGroup() |
|
{ |
|
s_BulletGroupCounter++; |
|
} |
|
|
|
//======================================================= |
|
// Remember this amount of damage that we dealt for stats |
|
//======================================================= |
|
void CCSPlayer::RecordDamageGiven( const char *szDamageTaker, int iDamageGiven ) |
|
{ |
|
FOR_EACH_LL( m_DamageGivenList, i ) |
|
{ |
|
if( Q_strncmp( szDamageTaker, m_DamageGivenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 ) |
|
{ |
|
m_DamageGivenList[i]->AddDamage( iDamageGiven, s_BulletGroupCounter ); |
|
return; |
|
} |
|
} |
|
|
|
CDamageRecord *record = new CDamageRecord( szDamageTaker, iDamageGiven, s_BulletGroupCounter ); |
|
int k = m_DamageGivenList.AddToTail(); |
|
m_DamageGivenList[k] = record; |
|
} |
|
|
|
//======================================================= |
|
// Remember this amount of damage that we took for stats |
|
//======================================================= |
|
void CCSPlayer::RecordDamageTaken( const char *szDamageDealer, int iDamageTaken ) |
|
{ |
|
FOR_EACH_LL( m_DamageTakenList, i ) |
|
{ |
|
if( Q_strncmp( szDamageDealer, m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 ) |
|
{ |
|
m_DamageTakenList[i]->AddDamage( iDamageTaken, s_BulletGroupCounter ); |
|
return; |
|
} |
|
} |
|
|
|
CDamageRecord *record = new CDamageRecord( szDamageDealer, iDamageTaken, s_BulletGroupCounter ); |
|
int k = m_DamageTakenList.AddToTail(); |
|
m_DamageTakenList[k] = record; |
|
} |
|
|
|
//======================================================= |
|
// Reset our damage given and taken counters |
|
//======================================================= |
|
void CCSPlayer::ResetDamageCounters() |
|
{ |
|
m_DamageGivenList.PurgeAndDeleteElements(); |
|
m_DamageTakenList.PurgeAndDeleteElements(); |
|
} |
|
|
|
//======================================================= |
|
// Output the damage that we dealt to other players |
|
//======================================================= |
|
void CCSPlayer::OutputDamageTaken( void ) |
|
{ |
|
bool bPrintHeader = true; |
|
CDamageRecord *pRecord; |
|
char buf[64]; |
|
int msg_dest = HUD_PRINTCONSOLE; |
|
|
|
FOR_EACH_LL( m_DamageTakenList, i ) |
|
{ |
|
if( bPrintHeader ) |
|
{ |
|
ClientPrint( this, msg_dest, "Player: %s1 - Damage Taken\n", GetPlayerName() ); |
|
ClientPrint( this, msg_dest, "-------------------------\n" ); |
|
bPrintHeader = false; |
|
} |
|
pRecord = m_DamageTakenList[i]; |
|
|
|
if( pRecord ) |
|
{ |
|
if (pRecord->GetNumHits() == 1) |
|
{ |
|
Q_snprintf( buf, sizeof(buf), "%d in %d hit", pRecord->GetDamage(), pRecord->GetNumHits() ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( buf, sizeof(buf), "%d in %d hits", pRecord->GetDamage(), pRecord->GetNumHits() ); |
|
} |
|
ClientPrint( this, msg_dest, "Damage Taken from \"%s1\" - %s2\n", pRecord->GetPlayerName(), buf ); |
|
} |
|
} |
|
} |
|
|
|
//======================================================= |
|
// Output the damage that we took from other players |
|
//======================================================= |
|
void CCSPlayer::OutputDamageGiven( void ) |
|
{ |
|
bool bPrintHeader = true; |
|
CDamageRecord *pRecord; |
|
char buf[64]; |
|
int msg_dest = HUD_PRINTCONSOLE; |
|
|
|
FOR_EACH_LL( m_DamageGivenList, i ) |
|
{ |
|
if( bPrintHeader ) |
|
{ |
|
ClientPrint( this, msg_dest, "Player: %s1 - Damage Given\n", GetPlayerName() ); |
|
ClientPrint( this, msg_dest, "-------------------------\n" ); |
|
bPrintHeader = false; |
|
} |
|
|
|
pRecord = m_DamageGivenList[i]; |
|
|
|
if( pRecord ) |
|
{ |
|
if (pRecord->GetNumHits() == 1) |
|
{ |
|
Q_snprintf( buf, sizeof(buf), "%d in %d hit", pRecord->GetDamage(), pRecord->GetNumHits() ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( buf, sizeof(buf), "%d in %d hits", pRecord->GetDamage(), pRecord->GetNumHits() ); |
|
} |
|
ClientPrint( this, msg_dest, "Damage Given to \"%s1\" - %s2\n", pRecord->GetPlayerName(), buf ); |
|
} |
|
} |
|
} |
|
|
|
void CCSPlayer::CreateViewModel( int index /*=0*/ ) |
|
{ |
|
Assert( index >= 0 && index < MAX_VIEWMODELS ); |
|
|
|
if ( GetViewModel( index ) ) |
|
return; |
|
|
|
CPredictedViewModel *vm = ( CPredictedViewModel * )CreateEntityByName( "predicted_viewmodel" ); |
|
if ( vm ) |
|
{ |
|
vm->SetAbsOrigin( GetAbsOrigin() ); |
|
vm->SetOwner( this ); |
|
vm->SetIndex( index ); |
|
DispatchSpawn( vm ); |
|
vm->FollowEntity( this, false ); |
|
m_hViewModel.Set( index, vm ); |
|
} |
|
} |
|
|
|
bool CCSPlayer::HasC4() const |
|
{ |
|
return ( Weapon_OwnsThisType( "weapon_c4" ) != NULL ); |
|
} |
|
|
|
int CCSPlayer::GetNextObserverSearchStartPoint( bool bReverse ) |
|
{ |
|
// If we are currently watching someone who is dead, they must have died while we were watching (since |
|
// a dead guy is not a valid pick to start watching). He was given his killer as an observer target |
|
// when he died, so let's start by trying to observe his killer. If we fail, we'll use the normal way. |
|
// And this is just the start point anyway, but we want to start the search here in case it is okay. |
|
if( m_hObserverTarget && !m_hObserverTarget->IsAlive() ) |
|
{ |
|
CCSPlayer *targetPlayer = ToCSPlayer(m_hObserverTarget); |
|
if( targetPlayer && targetPlayer->GetObserverTarget() ) |
|
return targetPlayer->GetObserverTarget()->entindex(); |
|
} |
|
|
|
return BaseClass::GetNextObserverSearchStartPoint( bReverse ); |
|
} |
|
|
|
void CCSPlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) |
|
{ |
|
BaseClass::PlayStepSound( vecOrigin, psurface, fvol, force ); |
|
|
|
if ( !sv_footsteps.GetFloat() ) |
|
return; |
|
|
|
if ( !psurface ) |
|
return; |
|
|
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_footstep" ); |
|
if ( event ) |
|
{ |
|
event->SetInt("userid", GetUserID() ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
|
|
m_bMadeFootstepNoise = true; |
|
} |
|
|
|
|
|
void CCSPlayer::SelectDeathPose( const CTakeDamageInfo &info ) |
|
{ |
|
MDLCACHE_CRITICAL_SECTION(); |
|
if ( !GetModelPtr() ) |
|
return; |
|
|
|
Activity aActivity = ACT_INVALID; |
|
int iDeathFrame = 0; |
|
|
|
SelectDeathPoseActivityAndFrame( this, info, m_LastHitGroup, aActivity, iDeathFrame ); |
|
if ( aActivity == ACT_INVALID ) |
|
{ |
|
SetDeathPose( ACT_INVALID ); |
|
SetDeathPoseFrame( 0 ); |
|
return; |
|
} |
|
|
|
SetDeathPose( SelectWeightedSequence( aActivity ) ); |
|
SetDeathPoseFrame( iDeathFrame ); |
|
} |
|
|
|
|
|
void CCSPlayer::HandleAnimEvent( animevent_t *pEvent ) |
|
{ |
|
if ( pEvent->event == 4001 || pEvent->event == 4002 ) |
|
{ |
|
// Ignore these for now - soon we will be playing footstep sounds based on these events |
|
// that mark footfalls in the anims. |
|
} |
|
else |
|
{ |
|
BaseClass::HandleAnimEvent( pEvent ); |
|
} |
|
} |
|
|
|
|
|
bool CCSPlayer::CanChangeName( void ) |
|
{ |
|
if ( IsBot() ) |
|
return true; |
|
|
|
// enforce the minimum interval |
|
if ( (m_flNameChangeHistory[0] + MIN_NAME_CHANGE_INTERVAL) >= gpGlobals->curtime ) |
|
{ |
|
return false; |
|
} |
|
|
|
// enforce that we dont do more than NAME_CHANGE_HISTORY_SIZE |
|
// changes within NAME_CHANGE_HISTORY_INTERVAL |
|
if ( (m_flNameChangeHistory[NAME_CHANGE_HISTORY_SIZE-1] + NAME_CHANGE_HISTORY_INTERVAL) >= gpGlobals->curtime ) |
|
{ |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void CCSPlayer::ChangeName( const char *pszNewName ) |
|
{ |
|
// make sure name is not too long |
|
char trimmedName[MAX_PLAYER_NAME_LENGTH]; |
|
Q_strncpy( trimmedName, pszNewName, sizeof( trimmedName ) ); |
|
|
|
const char *pszOldName = GetPlayerName(); |
|
|
|
// send colored message to everyone |
|
CReliableBroadcastRecipientFilter filter; |
|
UTIL_SayText2Filter( filter, this, false, "#Cstrike_Name_Change", pszOldName, trimmedName ); |
|
|
|
// broadcast event |
|
IGameEvent * event = gameeventmanager->CreateEvent( "player_changename" ); |
|
if ( event ) |
|
{ |
|
event->SetInt( "userid", GetUserID() ); |
|
event->SetString( "oldname", pszOldName ); |
|
event->SetString( "newname", trimmedName ); |
|
gameeventmanager->FireEvent( event ); |
|
} |
|
|
|
// change shared player name |
|
SetPlayerName( trimmedName ); |
|
|
|
// tell engine to use new name |
|
engine->ClientCommand( edict(), "name \"%s\"", trimmedName ); |
|
|
|
// remember time of name change |
|
for ( int i=NAME_CHANGE_HISTORY_SIZE-1; i>0; i-- ) |
|
{ |
|
m_flNameChangeHistory[i] = m_flNameChangeHistory[i-1]; |
|
} |
|
|
|
m_flNameChangeHistory[0] = gpGlobals->curtime; // last change |
|
} |
|
|
|
bool CCSPlayer::StartReplayMode( float fDelay, float fDuration, int iEntity ) |
|
{ |
|
if ( !BaseClass::StartReplayMode( fDelay, fDuration, iEntity ) ) |
|
return false; |
|
|
|
CSingleUserRecipientFilter filter( this ); |
|
filter.MakeReliable(); |
|
|
|
UserMessageBegin( filter, "KillCam" ); |
|
WRITE_BYTE( OBS_MODE_IN_EYE ); |
|
|
|
if ( m_hObserverTarget.Get() ) |
|
{ |
|
WRITE_BYTE( m_hObserverTarget.Get()->entindex() ); // first target |
|
WRITE_BYTE( entindex() ); //second target |
|
} |
|
else |
|
{ |
|
WRITE_BYTE( entindex() ); // first target |
|
WRITE_BYTE( 0 ); //second target |
|
} |
|
MessageEnd(); |
|
|
|
ClientPrint( this, HUD_PRINTCENTER, "Kill Cam Replay" ); |
|
|
|
return true; |
|
} |
|
|
|
void CCSPlayer::StopReplayMode() |
|
{ |
|
BaseClass::StopReplayMode(); |
|
|
|
CSingleUserRecipientFilter filter( this ); |
|
filter.MakeReliable(); |
|
|
|
UserMessageBegin( filter, "KillCam" ); |
|
WRITE_BYTE( OBS_MODE_NONE ); |
|
WRITE_BYTE( 0 ); |
|
WRITE_BYTE( 0 ); |
|
MessageEnd(); |
|
} |
|
|
|
void CCSPlayer::PlayUseDenySound() |
|
{ |
|
// Don't do a sound here because it can mute your footsteps giving you an advantage. |
|
// The CS:S content for this sound is silent anyways. |
|
//EmitSound( "Player.UseDeny" ); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
//============================================================================= |
|
|
|
// [menglish, tj] This is where we reset all the per-round information for achievements for this player |
|
void CCSPlayer::ResetRoundBasedAchievementVariables() |
|
{ |
|
m_KillingSpreeStartTime = -1; |
|
|
|
int numCTPlayers = 0, numTPlayers = 0; |
|
for (int i = 0; i < g_Teams.Count(); i++ ) |
|
{ |
|
if(g_Teams[i]) |
|
{ |
|
if ( g_Teams[i]->GetTeamNumber() == TEAM_CT ) |
|
numCTPlayers = g_Teams[i]->GetNumPlayers(); |
|
else if(g_Teams[i]->GetTeamNumber() == TEAM_TERRORIST) |
|
numTPlayers = g_Teams[i]->GetNumPlayers(); |
|
} |
|
} |
|
m_NumEnemiesKilledThisRound = 0; |
|
if(GetTeamNumber() == TEAM_CT) |
|
m_NumEnemiesAtRoundStart = numTPlayers; |
|
else if(GetTeamNumber() == TEAM_TERRORIST) |
|
m_NumEnemiesAtRoundStart = numCTPlayers; |
|
|
|
|
|
//Clear the previous owner field for currently held weapons |
|
CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_RIFLE )); |
|
if ( pWeapon ) |
|
{ |
|
pWeapon->SetPreviousOwner(NULL); |
|
} |
|
pWeapon = dynamic_cast< CWeaponCSBase * >(Weapon_GetSlot( WEAPON_SLOT_PISTOL)); |
|
if ( pWeapon ) |
|
{ |
|
pWeapon->SetPreviousOwner(NULL); |
|
} |
|
|
|
//Clear list of weapons used to get kills |
|
m_killWeapons.RemoveAll(); |
|
|
|
//Clear sliding window of kill times |
|
m_killTimes.RemoveAll(); |
|
|
|
//clear round kills |
|
m_enemyPlayersKilledThisRound.RemoveAll(); |
|
|
|
m_killsWhileBlind = 0; |
|
|
|
m_bSurvivedHeadshotDueToHelmet = false; |
|
|
|
m_gooseChaseStep = GC_NONE; |
|
m_defuseDefenseStep = DD_NONE; |
|
m_pGooseChaseDistractingPlayer = NULL; |
|
|
|
m_bMadeFootstepNoise = false; |
|
|
|
m_bombPickupTime = -1; |
|
|
|
m_bMadePurchseThisRound = false; |
|
|
|
m_bKilledDefuser = false; |
|
m_bKilledRescuer = false; |
|
m_maxGrenadeKills = 0; |
|
m_grenadeDamageTakenThisRound = 0; |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Needed for fun-fact implementation |
|
//============================================================================= |
|
|
|
WieldingKnifeAndKilledByGun(false); |
|
|
|
m_WeaponTypesUsed.RemoveAll(); |
|
|
|
m_bPickedUpDefuser = false; |
|
m_bDefusedWithPickedUpKit = false; |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
} |
|
|
|
|
|
/** |
|
* static public CCSPlayer::GetCSWeaponIDCausingDamage() |
|
* |
|
* Helper function to get the ID of the weapon used to kill a player. |
|
* This is slightly non-trivial because the grenade because a separate |
|
* entity when thrown. |
|
* |
|
* Parameters: |
|
* info - |
|
* |
|
* Returns: |
|
* int - |
|
*/ |
|
CSWeaponID CCSPlayer::GetWeaponIdCausingDamange( const CTakeDamageInfo &info ) |
|
{ |
|
CBaseEntity *pInflictor = info.GetInflictor(); |
|
CCSPlayer *pAttacker = ToCSPlayer(info.GetAttacker()); |
|
if (pAttacker == pInflictor) |
|
{ |
|
CWeaponCSBase* pAttackerWeapon = dynamic_cast< CWeaponCSBase * >(pAttacker->GetActiveWeapon()); |
|
if (!pAttackerWeapon) |
|
return WEAPON_NONE; |
|
|
|
return pAttackerWeapon->GetWeaponID(); |
|
} |
|
else if (pInflictor && V_strcmp(pInflictor->GetClassname(), "hegrenade_projectile") == 0) |
|
{ |
|
return WEAPON_HEGRENADE; |
|
} |
|
return WEAPON_NONE; |
|
} |
|
|
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] adding tracking for weapon used fun fact |
|
//============================================================================= |
|
void CCSPlayer::PlayerUsedFirearm( CBaseCombatWeapon* pBaseWeapon ) |
|
{ |
|
if ( pBaseWeapon ) |
|
{ |
|
CWeaponCSBase* pWeapon = dynamic_cast< CWeaponCSBase* >( pBaseWeapon ); |
|
|
|
if ( pWeapon ) |
|
{ |
|
CSWeaponType weaponType = pWeapon->GetCSWpnData().m_WeaponType; |
|
CSWeaponID weaponID = pWeapon->GetWeaponID(); |
|
|
|
if ( weaponType != WEAPONTYPE_KNIFE && weaponType != WEAPONTYPE_C4 && weaponType != WEAPONTYPE_GRENADE ) |
|
{ |
|
if ( m_WeaponTypesUsed.Find( weaponID ) == -1 ) |
|
{ |
|
// Add this weapon to the list of weapons used by the player |
|
m_WeaponTypesUsed.AddToTail( weaponID ); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
/** |
|
* public CCSPlayer::ProcessPlayerDeathAchievements() |
|
* |
|
* Do Achievement processing whenever a player is killed |
|
* |
|
* Parameters: |
|
* pAttacker - |
|
* pVictim - |
|
* info - |
|
*/ |
|
void CCSPlayer::ProcessPlayerDeathAchievements( CCSPlayer *pAttacker, CCSPlayer *pVictim, const CTakeDamageInfo &info ) |
|
{ |
|
Assert(pVictim != NULL); |
|
CBaseEntity *pInflictor = info.GetInflictor(); |
|
|
|
// all these achievements require a valid attacker on a different team |
|
if ( pAttacker != NULL && pVictim != NULL && pVictim->GetTeamNumber() != pAttacker->GetTeamNumber() ) |
|
{ |
|
// get the weapon used - some of the achievements will need this data |
|
CWeaponCSBase* pAttackerWeapon = dynamic_cast< CWeaponCSBase * >(pAttacker->GetActiveWeapon()); |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Fun-fact processing |
|
//============================================================================= |
|
|
|
CWeaponCSBase* pVictimWeapon = dynamic_cast< CWeaponCSBase* >(pVictim->GetActiveWeapon()); |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
CSWeaponID attackerWeaponId = GetWeaponIdCausingDamange(info); |
|
|
|
if (pVictim->m_bIsDefusing) |
|
{ |
|
pAttacker->AwardAchievement(CSKilledDefuser); |
|
pAttacker->m_bKilledDefuser = true; |
|
|
|
if (attackerWeaponId == WEAPON_HEGRENADE) |
|
{ |
|
pAttacker->AwardAchievement(CSKilledDefuserWithGrenade); |
|
} |
|
} |
|
|
|
// [pfreese] Achievement check for attacker killing player while reloading |
|
if (pVictim->IsReloading()) |
|
{ |
|
pAttacker->AwardAchievement(CSKillEnemyReloading); |
|
} |
|
|
|
if (pVictim->IsRescuing()) |
|
{ |
|
// Ensure the killer did not injure any hostages |
|
if ( !pAttacker->InjuredAHostage() && pVictim->GetNumFollowers() == g_Hostages.Count() ) |
|
{ |
|
pAttacker->AwardAchievement(CSKilledRescuer); |
|
pAttacker->m_bKilledRescuer = true; |
|
} |
|
} |
|
|
|
// [menglish] Achievement check for doing 95% or more damage to a player and having another player kill them |
|
FOR_EACH_LL( pVictim->m_DamageTakenList, i ) |
|
{ |
|
if( pVictim->m_DamageTakenList[i]->GetDamage() >= pVictim->GetMaxHealth() - AchievementConsts::DamageNoKill_MaxHealthLeftOnKill && |
|
Q_strncmp( pAttacker->GetPlayerName(), pVictim->m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) != 0 ) |
|
{ |
|
//Now find the player who did that amount of damage |
|
for ( int j = 1; j <= MAX_PLAYERS; j++ ) |
|
{ |
|
CBasePlayer *pPlayerIter = UTIL_PlayerByIndex( j ); |
|
|
|
if ( pPlayerIter && Q_strncmp( pPlayerIter->GetPlayerName(), pVictim->m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 && |
|
pPlayerIter->GetTeamNumber() != pVictim->GetTeamNumber() ) |
|
{ |
|
ToCSPlayer(pPlayerIter)->AwardAchievement(CSDamageNoKill); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
pAttacker->m_NumEnemiesKilledThisRound++; |
|
|
|
//store a list of kill times for spree tracking |
|
pAttacker->m_killTimes.AddToTail(gpGlobals->curtime); |
|
|
|
//Add the victim to the list of players killed this round |
|
pAttacker->m_enemyPlayersKilledThisRound.AddToTail(pVictim); |
|
|
|
//Calculate Avenging for all players the victim has killed |
|
for ( int avengedIndex = 0; avengedIndex < pVictim->m_enemyPlayersKilledThisRound.Count(); avengedIndex++ ) |
|
{ |
|
CCSPlayer* avengedPlayer = pVictim->m_enemyPlayersKilledThisRound[avengedIndex]; |
|
|
|
if (avengedPlayer) |
|
{ |
|
//Make sure you are avenging someone on your own team (This is the expected flow. Just here to avoid edge cases like team-switching). |
|
if (pAttacker->GetTeamNumber() == avengedPlayer->GetTeamNumber()) |
|
{ |
|
CCS_GameStats.Event_PlayerAvengedTeammate(pAttacker, pVictim->m_enemyPlayersKilledThisRound[avengedIndex]); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
//remove elements older than a certain time |
|
while (pAttacker->m_killTimes.Count() > 0 && pAttacker->m_killTimes[0] + AchievementConsts::KillingSpree_WindowTime < gpGlobals->curtime) |
|
{ |
|
pAttacker->m_killTimes.Remove(0); |
|
} |
|
|
|
//If we killed enough players in the time window, award the achievement |
|
if (pAttacker->m_killTimes.Count() >= AchievementConsts::KillingSpree_Kills) |
|
{ |
|
pAttacker->m_KillingSpreeStartTime = gpGlobals->curtime; |
|
pAttacker->AwardAchievement(CSKillingSpree); |
|
} |
|
|
|
// Did the attacker just kill someone on a killing spree? |
|
if (pVictim->m_KillingSpreeStartTime >= 0 && pVictim->m_KillingSpreeStartTime - gpGlobals->curtime <= AchievementConsts::KillingSpreeEnder_TimeWindow) |
|
{ |
|
pAttacker->AwardAchievement(CSKillingSpreeEnder); |
|
} |
|
|
|
//Check the "killed someone with their own weapon" achievement |
|
if (pAttackerWeapon && pAttackerWeapon->GetPreviousOwner() == pVictim) |
|
{ |
|
pAttacker->AwardAchievement(CSKillEnemyWithFormerGun); |
|
} |
|
|
|
//If this player has killed the entire team award him the achievement |
|
if (pAttacker->m_NumEnemiesKilledThisRound == pAttacker->m_NumEnemiesAtRoundStart && pAttacker->m_NumEnemiesKilledThisRound >= AchievementConsts::KillEnemyTeam_MinKills) |
|
{ |
|
pAttacker->AwardAchievement(CSKillEnemyTeam); |
|
} |
|
|
|
//If this is a posthumous kill award the achievement |
|
if (!pAttacker->IsAlive() && attackerWeaponId == WEAPON_HEGRENADE) |
|
{ |
|
CCS_GameStats.IncrementStat(pAttacker, CSSTAT_GRENADE_POSTHUMOUSKILLS, 1); |
|
ToCSPlayer(pAttacker)->AwardAchievement(CSPosthumousGrenadeKill); |
|
} |
|
|
|
if (pAttacker->GetActiveWeapon() && pAttacker->GetActiveWeapon()->Clip1() == 0 && pAttackerWeapon && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_SNIPER_RIFLE) |
|
{ |
|
if (pInflictor == pAttacker) |
|
{ |
|
pAttacker->AwardAchievement(CSKillEnemyLastBullet); |
|
CCS_GameStats.IncrementStat(pAttacker, CSSTAT_KILLS_WITH_LAST_ROUND, 1); |
|
} |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [dwenger] Fun-fact processing |
|
//============================================================================= |
|
|
|
if (pVictimWeapon && pVictimWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_KNIFE && pAttackerWeapon && |
|
pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_KNIFE && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_C4 && pAttackerWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_GRENADE) |
|
{ |
|
// Victim was wielding knife when killed by a gun |
|
pVictim->WieldingKnifeAndKilledByGun(true); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
//see if this is a unique weapon |
|
if (attackerWeaponId != WEAPON_NONE) |
|
{ |
|
if (pAttacker->m_killWeapons.Find(attackerWeaponId) == -1) |
|
{ |
|
pAttacker->m_killWeapons.AddToTail(attackerWeaponId); |
|
if (pAttacker->m_killWeapons.Count() >= AchievementConsts::KillsWithMultipleGuns_MinWeapons) |
|
{ |
|
pAttacker->AwardAchievement(CSKillsWithMultipleGuns); |
|
} |
|
} |
|
} |
|
|
|
//Check for kills while blind |
|
if (pAttacker->IsBlindForAchievement()) |
|
{ |
|
//if this is from a different blinding, restart the kill counter and set the time |
|
if (pAttacker->m_blindStartTime != pAttacker->m_firstKillBlindStartTime) |
|
{ |
|
pAttacker->m_killsWhileBlind = 0; |
|
pAttacker->m_firstKillBlindStartTime = pAttacker->m_blindStartTime; |
|
} |
|
|
|
++pAttacker->m_killsWhileBlind; |
|
if (pAttacker->m_killsWhileBlind >= AchievementConsts::KillEnemiesWhileBlind_Kills) |
|
{ |
|
pAttacker->AwardAchievement(CSKillEnemiesWhileBlind); |
|
} |
|
|
|
if (pAttacker->m_killsWhileBlind >= AchievementConsts::KillEnemiesWhileBlindHard_Kills) |
|
{ |
|
pAttacker->AwardAchievement(CSKillEnemiesWhileBlindHard); |
|
} |
|
} |
|
|
|
//Check sniper killing achievements |
|
bool victimZoomed = ( pVictim->GetFOV() != pVictim->GetDefaultFOV() ); |
|
bool attackerZoomed = ( pAttacker->GetFOV() != pAttacker->GetDefaultFOV() ); |
|
bool attackerUsedSniperRifle = pAttackerWeapon && pAttackerWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_SNIPER_RIFLE && pInflictor == pAttacker; |
|
if (victimZoomed && attackerUsedSniperRifle) |
|
{ |
|
pAttacker->AwardAchievement(CSKillSniperWithSniper); |
|
} |
|
|
|
if (attackerWeaponId == WEAPON_KNIFE && victimZoomed) |
|
{ |
|
pAttacker->AwardAchievement(CSKillSniperWithKnife); |
|
} |
|
if (attackerUsedSniperRifle && !attackerZoomed) |
|
{ |
|
pAttacker->AwardAchievement(CSHipShot); |
|
} |
|
|
|
//Kill a player at low health |
|
if (pAttacker->IsAlive() && pAttacker->GetHealth() <= AchievementConsts::KillWhenAtLowHealth_MaxHealth) |
|
{ |
|
pAttacker->AwardAchievement(CSKillWhenAtLowHealth); |
|
} |
|
|
|
//Kill a player with a knife during the pistol round |
|
if (CSGameRules()->IsPistolRound()) |
|
{ |
|
if (attackerWeaponId == WEAPON_KNIFE) |
|
{ |
|
pAttacker->AwardAchievement(CSPistolRoundKnifeKill); |
|
} |
|
} |
|
|
|
//[tj] Check for dual elites fight |
|
CWeaponCSBase* victimWeapon = pVictim->GetActiveCSWeapon(); |
|
|
|
if (victimWeapon) |
|
{ |
|
CSWeaponID victimWeaponID = victimWeapon->GetWeaponID(); |
|
|
|
if (attackerWeaponId == WEAPON_ELITE && victimWeaponID == WEAPON_ELITE) |
|
{ |
|
pAttacker->AwardAchievement(CSWinDualDuel); |
|
} |
|
} |
|
|
|
//[tj] See if the attacker or defender are in the air [sbodenbender] dont include ladders |
|
bool attackerInAir = pAttacker->GetMoveType() != MOVETYPE_LADDER && pAttacker->GetNearestSurfaceBelow(AchievementConsts::KillInAir_MinimumHeight) == NULL; |
|
bool victimInAir = pVictim->GetMoveType() != MOVETYPE_LADDER && pVictim->GetNearestSurfaceBelow(AchievementConsts::KillInAir_MinimumHeight) == NULL; |
|
|
|
if (attackerInAir) |
|
{ |
|
pAttacker->AwardAchievement(CSKillWhileInAir); |
|
} |
|
if (victimInAir) |
|
{ |
|
pAttacker->AwardAchievement(CSKillEnemyInAir); |
|
} |
|
if (attackerInAir && victimInAir) |
|
{ |
|
pAttacker->AwardAchievement(CSKillerAndEnemyInAir); |
|
} |
|
|
|
//[tj] advance to the next stage of the defuse defense achievement |
|
if (pAttacker->m_defuseDefenseStep == DD_STARTED_DEFUSE) |
|
{ |
|
pAttacker->m_defuseDefenseStep = DD_KILLED_TERRORIST; |
|
} |
|
|
|
if (pVictim->HasC4() && pVictim->GetBombPickuptime() + AchievementConsts::KillBombPickup_MaxTime > gpGlobals->curtime) |
|
{ |
|
pAttacker->AwardAchievement(CSKillBombPickup); |
|
} |
|
|
|
} |
|
|
|
|
|
//If you kill a friendly player while blind (from an enemy player), give the guy that blinded you an achievement |
|
if ( pAttacker != NULL && pVictim != NULL && pVictim->GetTeamNumber() == pAttacker->GetTeamNumber() && pAttacker->IsBlind()) |
|
{ |
|
CCSPlayer* flashbangAttacker = pAttacker->GetLastFlashbangAttacker(); |
|
if (flashbangAttacker && pAttacker->GetTeamNumber() != flashbangAttacker->GetTeamNumber()) |
|
{ |
|
flashbangAttacker->AwardAchievement(CSCauseFriendlyFireWithFlashbang); |
|
} |
|
} |
|
|
|
// do a scan to determine count of players still alive |
|
int livePlayerCount = 0; |
|
int teamCount[TEAM_MAXCOUNT]; |
|
int teamIgnoreCount[TEAM_MAXCOUNT]; |
|
memset(teamCount, 0, sizeof(teamCount)); |
|
memset(teamIgnoreCount, 0, sizeof(teamIgnoreCount)); |
|
CCSPlayer *pAlivePlayer = NULL; |
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); |
|
if (pPlayer) |
|
{ |
|
int teamNum = pPlayer->GetTeamNumber(); |
|
if ( teamNum >= 0 ) |
|
{ |
|
++teamCount[teamNum]; |
|
if (pPlayer->WasNotKilledNaturally()) |
|
{ |
|
teamIgnoreCount[teamNum]++; |
|
} |
|
} |
|
if (pPlayer->IsAlive() && pPlayer != pVictim) |
|
{ |
|
++livePlayerCount; |
|
pAlivePlayer = pPlayer; |
|
} |
|
} |
|
} |
|
|
|
// Achievement check for being the last player alive in a match |
|
if (pAlivePlayer) |
|
{ |
|
int alivePlayerTeam = pAlivePlayer->GetTeamNumber(); |
|
int alivePlayerOpposingTeam = alivePlayerTeam == TEAM_CT ? TEAM_TERRORIST : TEAM_CT; |
|
if (livePlayerCount == 1 |
|
&& CSGameRules()->m_iRoundWinStatus == WINNER_NONE |
|
&& teamCount[alivePlayerTeam] - teamIgnoreCount[alivePlayerTeam] >= AchievementConsts::LastPlayerAlive_MinPlayersOnTeam |
|
&& teamCount[alivePlayerOpposingTeam] - teamIgnoreCount[alivePlayerOpposingTeam] >= AchievementConsts::DefaultMinOpponentsForAchievement |
|
&& ( !(pAlivePlayer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED) )) |
|
{ |
|
pAlivePlayer->AwardAchievement(CSLastPlayerAlive); |
|
} |
|
} |
|
|
|
// [tj] Added hook into player killed stat that happens before weapon drop |
|
CCS_GameStats.Event_PlayerKilled_PreWeaponDrop(pVictim, info); |
|
} |
|
|
|
//[tj] traces up to maxTrace units down and returns any standable object it hits |
|
// (doesn't check slope for standability) |
|
CBaseEntity* CCSPlayer::GetNearestSurfaceBelow(float maxTrace) |
|
{ |
|
trace_t trace; |
|
Ray_t ray; |
|
|
|
Vector traceStart = this->GetAbsOrigin(); |
|
Vector traceEnd = traceStart; |
|
traceEnd.z -= maxTrace; |
|
|
|
Vector minExtent = this->m_Local.m_bDucked ? VEC_DUCK_HULL_MIN_SCALED( this ) : VEC_HULL_MIN_SCALED( this ); |
|
Vector maxExtent = this->m_Local.m_bDucked ? VEC_DUCK_HULL_MAX_SCALED( this ) : VEC_HULL_MAX_SCALED( this ); |
|
|
|
ray.Init( traceStart, traceEnd, minExtent, maxExtent ); |
|
UTIL_TraceRay( ray, MASK_PLAYERSOLID, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); |
|
|
|
return trace.m_pEnt; |
|
} |
|
|
|
// [tj] Added a way to react to the round ending before we reset. |
|
// It is important to note that this happens before the bomb explodes, so a player may die |
|
// after this from a bomb explosion or a late kill after a defuse/detonation/rescue. |
|
void CCSPlayer::OnRoundEnd(int winningTeam, int reason) |
|
{ |
|
if (winningTeam == WINNER_CT || winningTeam == WINNER_TER) |
|
{ |
|
int losingTeamId = (winningTeam == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT; |
|
|
|
CTeam* losingTeam = GetGlobalTeam(losingTeamId); |
|
|
|
int losingTeamPlayers = 0; |
|
|
|
if (losingTeam) |
|
{ |
|
losingTeamPlayers = losingTeam->GetNumPlayers(); |
|
|
|
int ignoreCount = 0; |
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i ); |
|
if (pPlayer) |
|
{ |
|
int teamNum = pPlayer->GetTeamNumber(); |
|
if ( teamNum == losingTeamId ) |
|
{ |
|
if (pPlayer->WasNotKilledNaturally()) |
|
{ |
|
ignoreCount++; |
|
} |
|
} |
|
|
|
} |
|
} |
|
|
|
losingTeamPlayers -= ignoreCount; |
|
} |
|
|
|
//Check fast round win achievement |
|
if ( IsAlive() && |
|
gpGlobals->curtime - CSGameRules()->GetRoundStartTime() < AchievementConsts::FastRoundWin_Time && |
|
GetTeamNumber() == winningTeam && |
|
losingTeamPlayers >= AchievementConsts::DefaultMinOpponentsForAchievement) |
|
{ |
|
AwardAchievement(CSFastRoundWin); |
|
} |
|
|
|
//Check goosechase achievement |
|
if (IsAlive() && reason == Target_Bombed && m_gooseChaseStep == GC_STOPPED_AFTER_GETTING_SHOT && m_pGooseChaseDistractingPlayer) |
|
{ |
|
m_pGooseChaseDistractingPlayer->AwardAchievement(CSGooseChase); |
|
} |
|
|
|
//Check Defuse Defense achievement |
|
if (IsAlive() && reason == Bomb_Defused && m_defuseDefenseStep == DD_KILLED_TERRORIST) |
|
{ |
|
AwardAchievement(CSDefuseDefense); |
|
} |
|
|
|
//Check silent win |
|
if (m_NumEnemiesKilledThisRound > 0 && GetTeamNumber() == winningTeam && !m_bMadeFootstepNoise) |
|
{ |
|
AwardAchievement(CSSilentWin); |
|
} |
|
|
|
//Process && Check "win rounds without buying" achievement |
|
if (GetTeamNumber() == winningTeam && !m_bMadePurchseThisRound) |
|
{ |
|
m_roundsWonWithoutPurchase++; |
|
if (m_roundsWonWithoutPurchase > AchievementConsts::WinRoundsWithoutBuying_Rounds) |
|
{ |
|
AwardAchievement(CSWinRoundsWithoutBuying); |
|
} |
|
} |
|
else |
|
{ |
|
m_roundsWonWithoutPurchase = 0; |
|
} |
|
} |
|
|
|
m_lastRoundResult = reason; |
|
} |
|
|
|
void CCSPlayer::OnPreResetRound() |
|
{ |
|
//Check headshot survival achievement |
|
if (IsAlive() && m_bSurvivedHeadshotDueToHelmet) |
|
{ |
|
AwardAchievement(CSSurvivedHeadshotDueToHelmet); |
|
} |
|
|
|
if (IsAlive() && m_grenadeDamageTakenThisRound > AchievementConsts::SurviveGrenade_MinDamage) |
|
{ |
|
AwardAchievement(CSSurviveGrenade); |
|
} |
|
|
|
|
|
//Check achievement for surviving attacks from multiple players. |
|
if (IsAlive()) |
|
{ |
|
int numberOfEnemyDamagers = GetNumEnemyDamagers(); |
|
|
|
if (numberOfEnemyDamagers >= AchievementConsts::SurviveManyAttacks_NumberDamagingPlayers) |
|
{ |
|
AwardAchievement(CSSurviveManyAttacks); |
|
} |
|
} |
|
} |
|
|
|
void CCSPlayer::OnCanceledDefuse() |
|
{ |
|
if (m_gooseChaseStep == GC_SHOT_DURING_DEFUSE) |
|
{ |
|
m_gooseChaseStep = GC_STOPPED_AFTER_GETTING_SHOT; |
|
} |
|
} |
|
|
|
|
|
void CCSPlayer::OnStartedDefuse() |
|
{ |
|
if (m_defuseDefenseStep == DD_NONE) |
|
{ |
|
m_defuseDefenseStep = DD_STARTED_DEFUSE; |
|
} |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::AttemptToExitFreezeCam( void ) |
|
{ |
|
float fEndFreezeTravel = m_flDeathTime + CS_DEATH_ANIMATION_TIME + spec_freeze_traveltime.GetFloat(); |
|
if ( gpGlobals->curtime < fEndFreezeTravel ) |
|
return; |
|
|
|
m_bAbortFreezeCam = true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets whether this player is dominating the specified other player |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::SetPlayerDominated( CCSPlayer *pPlayer, bool bDominated ) |
|
{ |
|
int iPlayerIndex = pPlayer->entindex(); |
|
m_bPlayerDominated.Set( iPlayerIndex, bDominated ); |
|
pPlayer->SetPlayerDominatingMe( this, bDominated ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets whether this player is being dominated by the other player |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::SetPlayerDominatingMe( CCSPlayer *pPlayer, bool bDominated ) |
|
{ |
|
int iPlayerIndex = pPlayer->entindex(); |
|
m_bPlayerDominatingMe.Set( iPlayerIndex, bDominated ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns whether this player is dominating the specified other player |
|
//----------------------------------------------------------------------------- |
|
bool CCSPlayer::IsPlayerDominated( int iPlayerIndex ) |
|
{ |
|
return m_bPlayerDominated.Get( iPlayerIndex ); |
|
} |
|
|
|
bool CCSPlayer::IsPlayerDominatingMe( int iPlayerIndex ) |
|
{ |
|
return m_bPlayerDominatingMe.Get( iPlayerIndex ); |
|
} |
|
|
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [menglish] MVP functions |
|
//============================================================================= |
|
|
|
void CCSPlayer::IncrementNumMVPs( CSMvpReason_t mvpReason ) |
|
{ |
|
//============================================================================= |
|
// HPE_BEGIN: |
|
// [Forrest] Allow MVP to be turned off for a server |
|
//============================================================================= |
|
if ( sv_nomvp.GetBool() ) |
|
{ |
|
Msg( "Round MVP disabled: sv_nomvp is set.\n" ); |
|
return; |
|
} |
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
m_iMVPs++; |
|
CCS_GameStats.Event_MVPEarned( this ); |
|
IGameEvent *mvpEvent = gameeventmanager->CreateEvent( "round_mvp" ); |
|
|
|
if ( mvpEvent ) |
|
{ |
|
mvpEvent->SetInt( "userid", GetUserID() ); |
|
mvpEvent->SetInt( "reason", mvpReason ); |
|
gameeventmanager->FireEvent( mvpEvent ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets the number of rounds this player has caused to be won for their team |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::SetNumMVPs( int iNumMVP ) |
|
{ |
|
m_iMVPs = iNumMVP; |
|
} |
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the number of rounds this player has caused to be won for their team |
|
//----------------------------------------------------------------------------- |
|
int CCSPlayer::GetNumMVPs() |
|
{ |
|
return m_iMVPs; |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Removes all nemesis relationships between this player and others |
|
//----------------------------------------------------------------------------- |
|
void CCSPlayer::RemoveNemesisRelationships() |
|
{ |
|
for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ ) |
|
{ |
|
CCSPlayer *pTemp = ToCSPlayer( UTIL_PlayerByIndex( i ) ); |
|
if ( pTemp && pTemp != this ) |
|
{ |
|
// set this player to be not dominating anyone else |
|
SetPlayerDominated( pTemp, false ); |
|
|
|
// set no one else to be dominating this player |
|
pTemp->SetPlayerDominated( this, false ); |
|
} |
|
} |
|
} |
|
|
|
void CCSPlayer::CheckMaxGrenadeKills(int grenadeKills) |
|
{ |
|
if (grenadeKills > m_maxGrenadeKills) |
|
{ |
|
m_maxGrenadeKills = grenadeKills; |
|
} |
|
} |
|
|
|
void CCSPlayer::CommitSuicide( bool bExplode /*= false*/, bool bForce /*= false*/ ) |
|
{ |
|
m_wasNotKilledNaturally = true; |
|
BaseClass::CommitSuicide(bExplode, bForce); |
|
} |
|
|
|
void CCSPlayer::CommitSuicide( const Vector &vecForce, bool bExplode /*= false*/, bool bForce /*= false*/ ) |
|
{ |
|
m_wasNotKilledNaturally = true; |
|
BaseClass::CommitSuicide(vecForce, bExplode, bForce); |
|
} |
|
|
|
int CCSPlayer::GetNumEnemyDamagers() |
|
{ |
|
int numberOfEnemyDamagers = 0; |
|
FOR_EACH_LL( m_DamageTakenList, i ) |
|
{ |
|
for ( int j = 1; j <= MAX_PLAYERS; j++ ) |
|
{ |
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex( j ); |
|
|
|
if ( pPlayer && V_strncmp( pPlayer->GetPlayerName(), m_DamageTakenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 && |
|
pPlayer->GetTeamNumber() != GetTeamNumber() ) |
|
{ |
|
numberOfEnemyDamagers++; |
|
} |
|
} |
|
} |
|
return numberOfEnemyDamagers; |
|
} |
|
|
|
|
|
int CCSPlayer::GetNumEnemiesDamaged() |
|
{ |
|
int numberOfEnemiesDamaged = 0; |
|
FOR_EACH_LL( m_DamageGivenList, i ) |
|
{ |
|
for ( int j = 1; j <= MAX_PLAYERS; j++ ) |
|
{ |
|
CBasePlayer *pPlayer = UTIL_PlayerByIndex( j ); |
|
|
|
if ( pPlayer && V_strncmp( pPlayer->GetPlayerName(), m_DamageGivenList[i]->GetPlayerName(), MAX_PLAYER_NAME_LENGTH ) == 0 && |
|
pPlayer->GetTeamNumber() != GetTeamNumber() ) |
|
{ |
|
numberOfEnemiesDamaged++; |
|
} |
|
} |
|
} |
|
return numberOfEnemiesDamaged; |
|
} |
|
|
|
//============================================================================= |
|
// HPE_END |
|
//============================================================================= |
|
|
|
void UTIL_AwardMoneyToTeam( int iAmount, int iTeam, CBaseEntity *pIgnore ) |
|
{ |
|
for ( int i = 1; i <= gpGlobals->maxClients; i++ ) |
|
{ |
|
CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i ); |
|
|
|
if ( !pPlayer ) |
|
continue; |
|
|
|
if ( pPlayer->GetTeamNumber() != iTeam ) |
|
continue; |
|
|
|
if ( pPlayer == pIgnore ) |
|
continue; |
|
|
|
pPlayer->AddAccount( iAmount ); |
|
} |
|
} |
|
|
|
|