diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/hl2mp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'game/server/hl2mp')
| -rw-r--r-- | game/server/hl2mp/grenade_satchel.cpp | 217 | ||||
| -rw-r--r-- | game/server/hl2mp/grenade_satchel.h | 56 | ||||
| -rw-r--r-- | game/server/hl2mp/grenade_tripmine.cpp | 276 | ||||
| -rw-r--r-- | game/server/hl2mp/grenade_tripmine.h | 56 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_bot_temp.cpp | 435 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_bot_temp.h | 19 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_client.cpp | 201 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_cvars.cpp | 25 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_cvars.h | 20 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_gameinterface.cpp | 32 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_gameinterface.h | 10 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_player.cpp | 1629 | ||||
| -rw-r--r-- | game/server/hl2mp/hl2mp_player.h | 176 | ||||
| -rw-r--r-- | game/server/hl2mp/te_hl2mp_shotgun_shot.cpp | 104 | ||||
| -rw-r--r-- | game/server/hl2mp/te_hl2mp_shotgun_shot.h | 26 |
15 files changed, 3282 insertions, 0 deletions
diff --git a/game/server/hl2mp/grenade_satchel.cpp b/game/server/hl2mp/grenade_satchel.cpp new file mode 100644 index 0000000..9b304ad --- /dev/null +++ b/game/server/hl2mp/grenade_satchel.cpp @@ -0,0 +1,217 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// + +#include "cbase.h" +#include "player.h" +#include "soundenvelope.h" +#include "engine/IEngineSound.h" +#include "explode.h" +#include "Sprite.h" +#include "grenade_satchel.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#define SLAM_SPRITE "sprites/redglow1.vmt" + +ConVar sk_plr_dmg_satchel ( "sk_plr_dmg_satchel","0"); +ConVar sk_npc_dmg_satchel ( "sk_npc_dmg_satchel","0"); +ConVar sk_satchel_radius ( "sk_satchel_radius","0"); + +BEGIN_DATADESC( CSatchelCharge ) + + DEFINE_FIELD( m_flNextBounceSoundTime, FIELD_TIME ), + DEFINE_FIELD( m_bInAir, FIELD_BOOLEAN ), + DEFINE_FIELD( m_vLastPosition, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_pMyWeaponSLAM, FIELD_CLASSPTR ), + DEFINE_FIELD( m_bIsAttached, FIELD_BOOLEAN ), + + // Function Pointers + DEFINE_THINKFUNC( SatchelThink ), + + // Inputs + DEFINE_INPUTFUNC( FIELD_VOID, "Explode", InputExplode), + +END_DATADESC() + +LINK_ENTITY_TO_CLASS( npc_satchel, CSatchelCharge ); + +//========================================================= +// Deactivate - do whatever it is we do to an orphaned +// satchel when we don't want it in the world anymore. +//========================================================= +void CSatchelCharge::Deactivate( void ) +{ + AddSolidFlags( FSOLID_NOT_SOLID ); + UTIL_Remove( this ); + + if ( m_hGlowSprite != NULL ) + { + UTIL_Remove( m_hGlowSprite ); + m_hGlowSprite = NULL; + } +} + + +void CSatchelCharge::Spawn( void ) +{ + Precache( ); + SetModel( "models/Weapons/w_slam.mdl" ); + + VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, false ); + SetMoveType( MOVETYPE_VPHYSICS ); + + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + + UTIL_SetSize(this, Vector( -6, -6, -2), Vector(6, 6, 2)); + + SetThink( &CSatchelCharge::SatchelThink ); + SetNextThink( gpGlobals->curtime + 0.1f ); + + m_flDamage = sk_plr_dmg_satchel.GetFloat(); + m_DmgRadius = sk_satchel_radius.GetFloat(); + m_takedamage = DAMAGE_YES; + m_iHealth = 1; + + SetGravity( UTIL_ScaleForGravity( 560 ) ); // slightly lower gravity + SetFriction( 1.0 ); + SetSequence( 1 ); + SetDamage( 150 ); + + m_bIsAttached = false; + m_bInAir = true; + m_flNextBounceSoundTime = 0; + + m_vLastPosition = vec3_origin; + + m_hGlowSprite = NULL; + CreateEffects(); +} + +//----------------------------------------------------------------------------- +// Purpose: Start up any effects for us +//----------------------------------------------------------------------------- +void CSatchelCharge::CreateEffects( void ) +{ + // Only do this once + if ( m_hGlowSprite != NULL ) + return; + + // Create a blinking light to show we're an active SLAM + m_hGlowSprite = CSprite::SpriteCreate( SLAM_SPRITE, GetAbsOrigin(), false ); + m_hGlowSprite->SetAttachment( this, 0 ); + m_hGlowSprite->SetTransparency( kRenderTransAdd, 255, 255, 255, 255, kRenderFxStrobeFast ); + m_hGlowSprite->SetBrightness( 255, 1.0f ); + m_hGlowSprite->SetScale( 0.2f, 0.5f ); + m_hGlowSprite->TurnOn(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CSatchelCharge::InputExplode( inputdata_t &inputdata ) +{ + ExplosionCreate( GetAbsOrigin() + Vector( 0, 0, 16 ), GetAbsAngles(), GetThrower(), GetDamage(), 200, + SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this); + + UTIL_Remove( this ); +} + + +void CSatchelCharge::SatchelThink( void ) +{ + // If attached resize so player can pick up off wall + if (m_bIsAttached) + { + UTIL_SetSize(this, Vector( -2, -2, -6), Vector(2, 2, 6)); + } + + // See if I can lose my owner (has dropper moved out of way?) + // Want do this so owner can shoot the satchel charge + if (GetOwnerEntity()) + { + trace_t tr; + Vector vUpABit = GetAbsOrigin(); + vUpABit.z += 5.0; + + CBaseEntity* saveOwner = GetOwnerEntity(); + SetOwnerEntity( NULL ); + UTIL_TraceEntity( this, GetAbsOrigin(), vUpABit, MASK_SOLID, &tr ); + if ( tr.startsolid || tr.fraction != 1.0 ) + { + SetOwnerEntity( saveOwner ); + } + } + + // Bounce movement code gets this think stuck occasionally so check if I've + // succeeded in moving, otherwise kill my motions. + else if ((GetAbsOrigin() - m_vLastPosition).LengthSqr()<1) + { + SetAbsVelocity( vec3_origin ); + + QAngle angVel = GetLocalAngularVelocity(); + angVel.y = 0; + SetLocalAngularVelocity( angVel ); + + // Clear think function + SetThink(NULL); + return; + } + m_vLastPosition= GetAbsOrigin(); + + StudioFrameAdvance( ); + SetNextThink( gpGlobals->curtime + 0.1f ); + + if (!IsInWorld()) + { + UTIL_Remove( this ); + return; + } + + // Is it attached to a wall? + if (m_bIsAttached) + { + return; + } +} + +void CSatchelCharge::Precache( void ) +{ + PrecacheModel("models/Weapons/w_slam.mdl"); + PrecacheModel(SLAM_SPRITE); +} + +void CSatchelCharge::BounceSound( void ) +{ + if (gpGlobals->curtime > m_flNextBounceSoundTime) + { + m_flNextBounceSoundTime = gpGlobals->curtime + 0.1; + } +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +// Input : +// Output : +//----------------------------------------------------------------------------- +CSatchelCharge::CSatchelCharge(void) +{ + m_vLastPosition.Init(); + m_pMyWeaponSLAM = NULL; +} + +CSatchelCharge::~CSatchelCharge(void) +{ + if ( m_hGlowSprite != NULL ) + { + UTIL_Remove( m_hGlowSprite ); + m_hGlowSprite = NULL; + } +} diff --git a/game/server/hl2mp/grenade_satchel.h b/game/server/hl2mp/grenade_satchel.h new file mode 100644 index 0000000..68d5d5a --- /dev/null +++ b/game/server/hl2mp/grenade_satchel.h @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Satchel Charge +// +// $Workfile: $ +// $Date: $ +// $NoKeywords: $ +//=============================================================================// + +#ifndef SATCHEL_H +#define SATCHEL_H + +#ifdef _WIN32 +#pragma once +#endif + +#include "basegrenade_shared.h" +#include "hl2mp/weapon_slam.h" + +class CSoundPatch; +class CSprite; + +class CSatchelCharge : public CBaseGrenade +{ +public: + DECLARE_CLASS( CSatchelCharge, CBaseGrenade ); + + void Spawn( void ); + void Precache( void ); + void BounceSound( void ); + void SatchelTouch( CBaseEntity *pOther ); + void SatchelThink( void ); + + // Input handlers + void InputExplode( inputdata_t &inputdata ); + + float m_flNextBounceSoundTime; + bool m_bInAir; + Vector m_vLastPosition; + +public: + CWeapon_SLAM* m_pMyWeaponSLAM; // Who shot me.. + bool m_bIsAttached; + void Deactivate( void ); + + CSatchelCharge(); + ~CSatchelCharge(); + + DECLARE_DATADESC(); + +private: + void CreateEffects( void ); + CHandle<CSprite> m_hGlowSprite; +}; + +#endif //SATCHEL_H diff --git a/game/server/hl2mp/grenade_tripmine.cpp b/game/server/hl2mp/grenade_tripmine.cpp new file mode 100644 index 0000000..e14a929 --- /dev/null +++ b/game/server/hl2mp/grenade_tripmine.cpp @@ -0,0 +1,276 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Implements the tripmine grenade. +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "beam_shared.h" +#include "shake.h" +#include "grenade_tripmine.h" +#include "vstdlib/random.h" +#include "engine/IEngineSound.h" +#include "explode.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +extern const char* g_pModelNameLaser; + +ConVar sk_plr_dmg_tripmine ( "sk_plr_dmg_tripmine","0"); +ConVar sk_npc_dmg_tripmine ( "sk_npc_dmg_tripmine","0"); +ConVar sk_tripmine_radius ( "sk_tripmine_radius","0"); + +LINK_ENTITY_TO_CLASS( npc_tripmine, CTripmineGrenade ); + +BEGIN_DATADESC( CTripmineGrenade ) + + DEFINE_FIELD( m_hOwner, FIELD_EHANDLE ), + DEFINE_FIELD( m_flPowerUp, FIELD_TIME ), + DEFINE_FIELD( m_vecDir, FIELD_VECTOR ), + DEFINE_FIELD( m_vecEnd, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_flBeamLength, FIELD_FLOAT ), + DEFINE_FIELD( m_pBeam, FIELD_CLASSPTR ), + DEFINE_FIELD( m_posOwner, FIELD_POSITION_VECTOR ), + DEFINE_FIELD( m_angleOwner, FIELD_VECTOR ), + + // Function Pointers + DEFINE_THINKFUNC( WarningThink ), + DEFINE_THINKFUNC( PowerupThink ), + DEFINE_THINKFUNC( BeamBreakThink ), + DEFINE_THINKFUNC( DelayDeathThink ), + +END_DATADESC() + +CTripmineGrenade::CTripmineGrenade() +{ + m_vecDir.Init(); + m_vecEnd.Init(); + m_posOwner.Init(); + m_angleOwner.Init(); +} + +void CTripmineGrenade::Spawn( void ) +{ + Precache( ); + // motor + SetMoveType( MOVETYPE_FLY ); + SetSolid( SOLID_BBOX ); + SetModel( "models/Weapons/w_slam.mdl" ); + + IPhysicsObject *pObject = VPhysicsInitNormal( SOLID_BBOX, GetSolidFlags() | FSOLID_TRIGGER, true ); + pObject->EnableMotion( false ); + SetCollisionGroup( COLLISION_GROUP_WEAPON ); + + SetCycle( 0.0f ); + m_nBody = 3; + m_flDamage = sk_plr_dmg_tripmine.GetFloat(); + m_DmgRadius = sk_tripmine_radius.GetFloat(); + + ResetSequenceInfo( ); + m_flPlaybackRate = 0; + + UTIL_SetSize(this, Vector( -4, -4, -2), Vector(4, 4, 2)); + + m_flPowerUp = gpGlobals->curtime + 2.0; + + SetThink( &CTripmineGrenade::PowerupThink ); + SetNextThink( gpGlobals->curtime + 0.2 ); + + m_takedamage = DAMAGE_YES; + + m_iHealth = 1; + + EmitSound( "TripmineGrenade.Place" ); + SetDamage ( 200 ); + + // Tripmine sits at 90 on wall so rotate back to get m_vecDir + QAngle angles = GetAbsAngles(); + angles.x -= 90; + + AngleVectors( angles, &m_vecDir ); + m_vecEnd = GetAbsOrigin() + m_vecDir * 2048; + + AddEffects( EF_NOSHADOW ); +} + + +void CTripmineGrenade::Precache( void ) +{ + PrecacheModel("models/Weapons/w_slam.mdl"); + + PrecacheScriptSound( "TripmineGrenade.Place" ); + PrecacheScriptSound( "TripmineGrenade.Activate" ); +} + + +void CTripmineGrenade::WarningThink( void ) +{ + // set to power up + SetThink( &CTripmineGrenade::PowerupThink ); + SetNextThink( gpGlobals->curtime + 1.0f ); +} + + +void CTripmineGrenade::PowerupThink( void ) +{ + if (gpGlobals->curtime > m_flPowerUp) + { + MakeBeam( ); + RemoveSolidFlags( FSOLID_NOT_SOLID ); + m_bIsLive = true; + + // play enabled sound + EmitSound( "TripmineGrenade.Activate" ); + } + SetNextThink( gpGlobals->curtime + 0.1f ); +} + + +void CTripmineGrenade::KillBeam( void ) +{ + if ( m_pBeam ) + { + UTIL_Remove( m_pBeam ); + m_pBeam = NULL; + } +} + + +void CTripmineGrenade::MakeBeam( void ) +{ + trace_t tr; + + UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + + m_flBeamLength = tr.fraction; + + + + // If I hit a living thing, send the beam through me so it turns on briefly + // and then blows the living thing up + CBaseEntity *pEntity = tr.m_pEnt; + CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity ); + + // Draw length is not the beam length if entity is in the way + float drawLength = tr.fraction; + if (pBCC) + { + SetOwnerEntity( pBCC ); + UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); + m_flBeamLength = tr.fraction; + SetOwnerEntity( NULL ); + + } + + // set to follow laser spot + SetThink( &CTripmineGrenade::BeamBreakThink ); + + // Delay first think slightly so beam has time + // to appear if person right in front of it + SetNextThink( gpGlobals->curtime + 1.0f ); + + Vector vecTmpEnd = GetLocalOrigin() + m_vecDir * 2048 * drawLength; + + m_pBeam = CBeam::BeamCreate( g_pModelNameLaser, 0.35 ); + m_pBeam->PointEntInit( vecTmpEnd, this ); + m_pBeam->SetColor( 255, 55, 52 ); + m_pBeam->SetScrollRate( 25.6 ); + m_pBeam->SetBrightness( 64 ); + + int beamAttach = LookupAttachment("beam_attach"); + m_pBeam->SetEndAttachment( beamAttach ); +} + + +void CTripmineGrenade::BeamBreakThink( void ) +{ + // See if I can go solid yet (has dropper moved out of way?) + if (IsSolidFlagSet( FSOLID_NOT_SOLID )) + { + trace_t tr; + Vector vUpBit = GetAbsOrigin(); + vUpBit.z += 5.0; + + UTIL_TraceEntity( this, GetAbsOrigin(), vUpBit, MASK_SHOT, &tr ); + if ( !tr.startsolid && (tr.fraction == 1.0) ) + { + RemoveSolidFlags( FSOLID_NOT_SOLID ); + } + } + + trace_t tr; + + // NOT MASK_SHOT because we want only simple hit boxes + UTIL_TraceLine( GetAbsOrigin(), m_vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); + + // ALERT( at_console, "%f : %f\n", tr.flFraction, m_flBeamLength ); + + // respawn detect. + if ( !m_pBeam ) + { + MakeBeam( ); + if ( tr.m_pEnt ) + m_hOwner = tr.m_pEnt; // reset owner too + } + + + CBaseEntity *pEntity = tr.m_pEnt; + CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( pEntity ); + + if (pBCC || fabs( m_flBeamLength - tr.fraction ) > 0.001) + { + m_iHealth = 0; + Event_Killed( CTakeDamageInfo( (CBaseEntity*)m_hOwner, this, 100, GIB_NORMAL ) ); + return; + } + + SetNextThink( gpGlobals->curtime + 0.05f ); +} + +#if 0 // FIXME: OnTakeDamage_Alive() is no longer called now that base grenade derives from CBaseAnimating +int CTripmineGrenade::OnTakeDamage_Alive( const CTakeDamageInfo &info ) +{ + if (gpGlobals->curtime < m_flPowerUp && info.GetDamage() < m_iHealth) + { + // disable + // Create( "weapon_tripmine", GetLocalOrigin() + m_vecDir * 24, GetAngles() ); + SetThink( &CTripmineGrenade::SUB_Remove ); + SetNextThink( gpGlobals->curtime + 0.1f ); + KillBeam(); + return FALSE; + } + return BaseClass::OnTakeDamage_Alive( info ); +} +#endif + +//----------------------------------------------------------------------------- +// Purpose: +// Input : +// Output : +//----------------------------------------------------------------------------- +void CTripmineGrenade::Event_Killed( const CTakeDamageInfo &info ) +{ + m_takedamage = DAMAGE_NO; + + SetThink( &CTripmineGrenade::DelayDeathThink ); + SetNextThink( gpGlobals->curtime + 0.25 ); + + EmitSound( "TripmineGrenade.StopSound" ); +} + + +void CTripmineGrenade::DelayDeathThink( void ) +{ + KillBeam(); + trace_t tr; + UTIL_TraceLine ( GetAbsOrigin() + m_vecDir * 8, GetAbsOrigin() - m_vecDir * 64, MASK_SOLID, this, COLLISION_GROUP_NONE, & tr); + UTIL_ScreenShake( GetAbsOrigin(), 25.0, 150.0, 1.0, 750, SHAKE_START ); + + ExplosionCreate( GetAbsOrigin() + m_vecDir * 8, GetAbsAngles(), m_hOwner, GetDamage(), 200, + SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE, 0.0f, this); + + UTIL_Remove( this ); +} + diff --git a/game/server/hl2mp/grenade_tripmine.h b/game/server/hl2mp/grenade_tripmine.h new file mode 100644 index 0000000..e188709 --- /dev/null +++ b/game/server/hl2mp/grenade_tripmine.h @@ -0,0 +1,56 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef GRENADE_TRIPMINE_H +#define GRENADE_TRIPMINE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "basegrenade_shared.h" + +class CBeam; + + +class CTripmineGrenade : public CBaseGrenade +{ +public: + DECLARE_CLASS( CTripmineGrenade, CBaseGrenade ); + + CTripmineGrenade(); + void Spawn( void ); + void Precache( void ); + +#if 0 // FIXME: OnTakeDamage_Alive() is no longer called now that base grenade derives from CBaseAnimating + int OnTakeDamage_Alive( const CTakeDamageInfo &info ); +#endif + void WarningThink( void ); + void PowerupThink( void ); + void BeamBreakThink( void ); + void DelayDeathThink( void ); + void Event_Killed( const CTakeDamageInfo &info ); + + void MakeBeam( void ); + void KillBeam( void ); + +public: + EHANDLE m_hOwner; + +private: + float m_flPowerUp; + Vector m_vecDir; + Vector m_vecEnd; + float m_flBeamLength; + + CBeam *m_pBeam; + Vector m_posOwner; + Vector m_angleOwner; + + DECLARE_DATADESC(); +}; + +#endif // GRENADE_TRIPMINE_H diff --git a/game/server/hl2mp/hl2mp_bot_temp.cpp b/game/server/hl2mp/hl2mp_bot_temp.cpp new file mode 100644 index 0000000..054eea4 --- /dev/null +++ b/game/server/hl2mp/hl2mp_bot_temp.cpp @@ -0,0 +1,435 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Basic BOT handling. +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// + + +#include "cbase.h" +#include "player.h" +#include "hl2mp_player.h" +#include "in_buttons.h" +#include "movehelper_server.h" + +void ClientPutInServer( edict_t *pEdict, const char *playername ); +void Bot_Think( CHL2MP_Player *pBot ); + +#ifdef DEBUG + +ConVar bot_forcefireweapon( "bot_forcefireweapon", "", 0, "Force bots with the specified weapon to fire." ); +ConVar bot_forceattack2( "bot_forceattack2", "0", 0, "When firing, use attack2." ); +ConVar bot_forceattackon( "bot_forceattackon", "0", 0, "When firing, don't tap fire, hold it down." ); +ConVar bot_flipout( "bot_flipout", "0", 0, "When on, all bots fire their guns." ); +ConVar bot_defend( "bot_defend", "0", 0, "Set to a team number, and that team will all keep their combat shields raised." ); +ConVar bot_changeclass( "bot_changeclass", "0", 0, "Force all bots to change to the specified class." ); +ConVar bot_zombie( "bot_zombie", "0", 0, "Brraaaaaiiiins." ); +static ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "0", 0, "Offsets the bot yaw." ); +ConVar bot_attack( "bot_attack", "1", 0, "Shoot!" ); + +ConVar bot_sendcmd( "bot_sendcmd", "", 0, "Forces bots to send the specified command." ); + +ConVar bot_crouch( "bot_crouch", "0", 0, "Bot crouches" ); + +#ifdef NEXT_BOT +extern ConVar bot_mimic; +#else +ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." ); +#endif + +static int BotNumber = 1; +static int g_iNextBotTeam = -1; +static int g_iNextBotClass = -1; + +typedef struct +{ + bool backwards; + + float nextturntime; + bool lastturntoright; + + float nextstrafetime; + float sidemove; + + QAngle forwardAngle; + QAngle lastAngles; + + float m_flJoinTeamTime; + int m_WantedTeam; + int m_WantedClass; +} botdata_t; + +static botdata_t g_BotData[ MAX_PLAYERS ]; + + +//----------------------------------------------------------------------------- +// Purpose: Create a new Bot and put it in the game. +// Output : Pointer to the new Bot, or NULL if there's no free clients. +//----------------------------------------------------------------------------- +CBasePlayer *BotPutInServer( bool bFrozen, int iTeam ) +{ + g_iNextBotTeam = iTeam; + + char botname[ 64 ]; + Q_snprintf( botname, sizeof( botname ), "Bot%02i", BotNumber ); + + // This is an evil hack, but we use it to prevent sv_autojointeam from kicking in. + + edict_t *pEdict = engine->CreateFakeClient( botname ); + + if (!pEdict) + { + Msg( "Failed to create Bot.\n"); + return NULL; + } + + // Allocate a CBasePlayer for the bot, and call spawn + //ClientPutInServer( pEdict, botname ); + CHL2MP_Player *pPlayer = ((CHL2MP_Player *)CBaseEntity::Instance( pEdict )); + pPlayer->ClearFlags(); + pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT ); + + if ( bFrozen ) + pPlayer->AddEFlags( EFL_BOT_FROZEN ); + + BotNumber++; + + g_BotData[pPlayer->entindex()-1].m_WantedTeam = iTeam; + g_BotData[pPlayer->entindex()-1].m_flJoinTeamTime = gpGlobals->curtime + 0.3; + + return pPlayer; +} + +//----------------------------------------------------------------------------- +// Purpose: Run through all the Bots in the game and let them think. +//----------------------------------------------------------------------------- +void Bot_RunAll( void ) +{ + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CHL2MP_Player *pPlayer = ToHL2MPPlayer( UTIL_PlayerByIndex( i ) ); + + if ( pPlayer && (pPlayer->GetFlags() & FL_FAKECLIENT) ) + { + Bot_Think( pPlayer ); + } + } +} + +bool RunMimicCommand( CUserCmd& cmd ) +{ + if ( bot_mimic.GetInt() <= 0 ) + return false; + + if ( bot_mimic.GetInt() > gpGlobals->maxClients ) + return false; + + + CBasePlayer *pPlayer = UTIL_PlayerByIndex( bot_mimic.GetInt() ); + if ( !pPlayer ) + return false; + + if ( !pPlayer->GetLastUserCommand() ) + return false; + + cmd = *pPlayer->GetLastUserCommand(); + cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Simulates a single frame of movement for a player +// Input : *fakeclient - +// *viewangles - +// forwardmove - +// sidemove - +// upmove - +// buttons - +// impulse - +// msec - +// Output : virtual void +//----------------------------------------------------------------------------- +static void RunPlayerMove( CHL2MP_Player *fakeclient, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime ) +{ + if ( !fakeclient ) + return; + + 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; + fakeclient->SetTimeBase( flTimeBase ); + + Q_memset( &cmd, 0, sizeof( cmd ) ); + + if ( !RunMimicCommand( cmd ) && !bot_zombie.GetBool() ) + { + VectorCopy( viewangles, cmd.viewangles ); + cmd.forwardmove = forwardmove; + cmd.sidemove = sidemove; + cmd.upmove = upmove; + cmd.buttons = buttons; + cmd.impulse = impulse; + cmd.random_seed = random->RandomInt( 0, 0x7fffffff ); + } + + if( bot_crouch.GetInt() ) + cmd.buttons |= IN_DUCK; + + if ( bot_attack.GetBool() ) + cmd.buttons |= IN_ATTACK; + + MoveHelperServer()->SetHost( fakeclient ); + fakeclient->PlayerRunCommand( &cmd, MoveHelperServer() ); + + // save off the last good usercmd + fakeclient->SetLastUserCommand( cmd ); + + // Clear out any fixangle that has been set + fakeclient->pl.fixangle = FIXANGLE_NONE; + + // Restore the globals.. + gpGlobals->frametime = flOldFrametime; + gpGlobals->curtime = flOldCurtime; +} + +//----------------------------------------------------------------------------- +// Purpose: Run this Bot's AI for one frame. +//----------------------------------------------------------------------------- +void Bot_Think( CHL2MP_Player *pBot ) +{ + // Make sure we stay being a bot + pBot->AddFlag( FL_FAKECLIENT ); + + botdata_t *botdata = &g_BotData[ ENTINDEX( pBot->edict() ) - 1 ]; + + QAngle vecViewAngles; + float forwardmove = 0.0; + float sidemove = botdata->sidemove; + float upmove = 0.0; + unsigned short buttons = 0; + byte impulse = 0; + float frametime = gpGlobals->frametime; + + vecViewAngles = pBot->GetLocalAngles(); + + + // Create some random values + if ( pBot->IsAlive() && (pBot->GetSolid() == SOLID_BBOX) ) + { + trace_t trace; + + // Stop when shot + if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) ) + { + if ( pBot->m_iHealth == 100 ) + { + forwardmove = 600 * ( botdata->backwards ? -1 : 1 ); + if ( botdata->sidemove != 0.0f ) + { + forwardmove *= random->RandomFloat( 0.1, 1.0f ); + } + } + else + { + forwardmove = 0; + } + } + + // Only turn if I haven't been hurt + if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_iHealth == 100 ) + { + Vector vecEnd; + Vector forward; + + QAngle angle; + float angledelta = 15.0; + + int maxtries = (int)360.0/angledelta; + + if ( botdata->lastturntoright ) + { + angledelta = -angledelta; + } + + angle = pBot->GetLocalAngles(); + + Vector vecSrc; + while ( --maxtries >= 0 ) + { + AngleVectors( angle, &forward ); + + vecSrc = pBot->GetLocalOrigin() + Vector( 0, 0, 36 ); + + vecEnd = vecSrc + forward * 10; + + UTIL_TraceHull( vecSrc, vecEnd, VEC_HULL_MIN_SCALED( pBot ), VEC_HULL_MAX_SCALED( pBot ), + MASK_PLAYERSOLID, pBot, COLLISION_GROUP_NONE, &trace ); + + if ( trace.fraction == 1.0 ) + { + if ( gpGlobals->curtime < botdata->nextturntime ) + { + break; + } + } + + angle.y += angledelta; + + if ( angle.y > 180 ) + angle.y -= 360; + else if ( angle.y < -180 ) + angle.y += 360; + + botdata->nextturntime = gpGlobals->curtime + 2.0; + botdata->lastturntoright = random->RandomInt( 0, 1 ) == 0 ? true : false; + + botdata->forwardAngle = angle; + botdata->lastAngles = angle; + + } + + + if ( gpGlobals->curtime >= botdata->nextstrafetime ) + { + botdata->nextstrafetime = gpGlobals->curtime + 1.0f; + + if ( random->RandomInt( 0, 5 ) == 0 ) + { + botdata->sidemove = -600.0f + 1200.0f * random->RandomFloat( 0, 2 ); + } + else + { + botdata->sidemove = 0; + } + sidemove = botdata->sidemove; + + if ( random->RandomInt( 0, 20 ) == 0 ) + { + botdata->backwards = true; + } + else + { + botdata->backwards = false; + } + } + + pBot->SetLocalAngles( angle ); + vecViewAngles = angle; + } + + // Is my team being forced to defend? + if ( bot_defend.GetInt() == pBot->GetTeamNumber() ) + { + buttons |= IN_ATTACK2; + } + // If bots are being forced to fire a weapon, see if I have it + else if ( bot_forcefireweapon.GetString() ) + { + CBaseCombatWeapon *pWeapon = pBot->Weapon_OwnsThisType( bot_forcefireweapon.GetString() ); + if ( pWeapon ) + { + // Switch to it if we don't have it out + CBaseCombatWeapon *pActiveWeapon = pBot->GetActiveWeapon(); + + // Switch? + if ( pActiveWeapon != pWeapon ) + { + pBot->Weapon_Switch( pWeapon ); + } + else + { + // Start firing + // Some weapons require releases, so randomise firing + if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) ) + { + buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK; + } + } + } + } + + if ( bot_flipout.GetInt() ) + { + if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) ) + { + buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK; + } + } + + if ( strlen( bot_sendcmd.GetString() ) > 0 ) + { + //send the cmd from this bot + CCommand args; + args.Tokenize( bot_sendcmd.GetString() ); + pBot->ClientCommand( args ); + + bot_sendcmd.SetValue(""); + } + } + else + { + // Wait for Reinforcement wave + if ( !pBot->IsAlive() ) + { + // Try hitting my buttons occasionally + if ( random->RandomInt( 0, 100 ) > 80 ) + { + // Respawn the bot + if ( random->RandomInt( 0, 1 ) == 0 ) + { + buttons |= IN_JUMP; + } + else + { + buttons = 0; + } + } + } + } + + if ( bot_flipout.GetInt() >= 2 ) + { + + QAngle angOffset = RandomAngle( -1, 1 ); + + botdata->lastAngles += angOffset; + + for ( int i = 0 ; i < 2; i++ ) + { + if ( fabs( botdata->lastAngles[ i ] - botdata->forwardAngle[ i ] ) > 15.0f ) + { + if ( botdata->lastAngles[ i ] > botdata->forwardAngle[ i ] ) + { + botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] + 15; + } + else + { + botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] - 15; + } + } + } + + botdata->lastAngles[ 2 ] = 0; + + pBot->SetLocalAngles( botdata->lastAngles ); + } + + RunPlayerMove( pBot, pBot->GetLocalAngles(), forwardmove, sidemove, upmove, buttons, impulse, frametime ); +} + + + + +#endif + diff --git a/game/server/hl2mp/hl2mp_bot_temp.h b/game/server/hl2mp/hl2mp_bot_temp.h new file mode 100644 index 0000000..f78e63e --- /dev/null +++ b/game/server/hl2mp/hl2mp_bot_temp.h @@ -0,0 +1,19 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// +#ifndef BOT_BASE_H +#define BOT_BASE_H +#ifdef _WIN32 +#pragma once +#endif + + +// If iTeam or iClass is -1, then a team or class is randomly chosen. +CBasePlayer *BotPutInServer( bool bFrozen, int iTeam ); + + +#endif // BOT_BASE_H + diff --git a/game/server/hl2mp/hl2mp_client.cpp b/game/server/hl2mp/hl2mp_client.cpp new file mode 100644 index 0000000..571fbb8 --- /dev/null +++ b/game/server/hl2mp/hl2mp_client.cpp @@ -0,0 +1,201 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +/* + +===== tf_client.cpp ======================================================== + + HL2 client/server game specific stuff + +*/ + +#include "cbase.h" +#include "hl2mp_player.h" +#include "hl2mp_gamerules.h" +#include "gamerules.h" +#include "teamplay_gamerules.h" +#include "entitylist.h" +#include "physics.h" +#include "game.h" +#include "player_resource.h" +#include "engine/IEngineSound.h" +#include "team.h" +#include "viewport_panel_names.h" + +#include "tier0/vprof.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +void Host_Say( edict_t *pEdict, bool teamonly ); + +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." ); + +extern CBaseEntity* FindPickerEntityClass( CBasePlayer *pPlayer, char *classname ); +extern bool g_fGameOver; + +void FinishClientPutInServer( CHL2MP_Player *pPlayer ) +{ + pPlayer->InitialSpawn(); + pPlayer->Spawn(); + + + char sName[128]; + Q_strncpy( sName, pPlayer->GetPlayerName(), sizeof( sName ) ); + + // First parse the name and remove any %'s + for ( char *pApersand = sName; pApersand != NULL && *pApersand != 0; pApersand++ ) + { + // Replace it with a space + if ( *pApersand == '%' ) + *pApersand = ' '; + } + + // notify other clients of player joining the game + UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "#Game_connected", sName[0] != 0 ? sName : "<unconnected>" ); + + if ( HL2MPRules()->IsTeamplay() == true ) + { + ClientPrint( pPlayer, HUD_PRINTTALK, "You are on team %s1\n", pPlayer->GetTeam()->GetName() ); + } + + const ConVar *hostname = cvar->FindVar( "hostname" ); + const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY"; + + 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->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() ); + + pPlayer->ShowViewPortPanel( PANEL_INFO, true, data ); + + data->deleteThis(); +} + +/* +=========== +ClientPutInServer + +called each time a player is spawned into the game +============ +*/ +void ClientPutInServer( edict_t *pEdict, const char *playername ) +{ + // Allocate a CBaseTFPlayer for pev, and call spawn + CHL2MP_Player *pPlayer = CHL2MP_Player::CreatePlayer( "player", pEdict ); + pPlayer->SetPlayerName( playername ); +} + + +void ClientActive( edict_t *pEdict, bool bLoadGame ) +{ + // Can't load games in CS! + Assert( !bLoadGame ); + + CHL2MP_Player *pPlayer = ToHL2MPPlayer( CBaseEntity::Instance( pEdict ) ); + FinishClientPutInServer( pPlayer ); +} + + +/* +=============== +const char *GetGameDescription() + +Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2 +=============== +*/ +const char *GetGameDescription() +{ + if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized + return g_pGameRules->GetGameDescription(); + else + return "Half-Life 2 Deathmatch"; +} + +//----------------------------------------------------------------------------- +// Purpose: Given a player and optional name returns the entity of that +// classname that the player is nearest facing +// +// Input : +// Output : +//----------------------------------------------------------------------------- +CBaseEntity* FindEntity( edict_t *pEdict, char *classname) +{ + // If no name was given set bits based on the picked + if (FStrEq(classname,"")) + { + return (FindPickerEntityClass( static_cast<CBasePlayer*>(GetContainingEntity(pEdict)), classname )); + } + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: Precache game-specific models & sounds +//----------------------------------------------------------------------------- +void ClientGamePrecache( void ) +{ + CBaseEntity::PrecacheModel("models/player.mdl"); + CBaseEntity::PrecacheModel( "models/gibs/agibs.mdl" ); + CBaseEntity::PrecacheModel ("models/weapons/v_hands.mdl"); + + CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowAmmo" ); + CBaseEntity::PrecacheScriptSound( "HUDQuickInfo.LowHealth" ); + + CBaseEntity::PrecacheScriptSound( "FX_AntlionImpact.ShellImpact" ); + CBaseEntity::PrecacheScriptSound( "Missile.ShotDown" ); + CBaseEntity::PrecacheScriptSound( "Bullets.DefaultNearmiss" ); + CBaseEntity::PrecacheScriptSound( "Bullets.GunshipNearmiss" ); + CBaseEntity::PrecacheScriptSound( "Bullets.StriderNearmiss" ); + + CBaseEntity::PrecacheScriptSound( "Geiger.BeepHigh" ); + CBaseEntity::PrecacheScriptSound( "Geiger.BeepLow" ); +} + + +// called by ClientKill and DeadThink +void respawn( CBaseEntity *pEdict, bool fCopyCorpse ) +{ + CHL2MP_Player *pPlayer = ToHL2MPPlayer( pEdict ); + + if ( pPlayer ) + { + if ( gpGlobals->curtime > pPlayer->GetDeathTime() + DEATH_ANIMATION_TIME ) + { + // respawn player + pPlayer->Spawn(); + } + else + { + pPlayer->SetNextThink( gpGlobals->curtime + 0.1f ); + } + } +} + +void GameStartFrame( void ) +{ + VPROF("GameStartFrame()"); + if ( g_fGameOver ) + return; + + gpGlobals->teamplay = (teamplay.GetInt() != 0); + +#ifdef DEBUG + extern void Bot_RunAll(); + Bot_RunAll(); +#endif +} + +//========================================================= +// instantiate the proper game rules object +//========================================================= +void InstallGameRules() +{ + // vanilla deathmatch + CreateGameRulesObject( "CHL2MPRules" ); +} + diff --git a/game/server/hl2mp/hl2mp_cvars.cpp b/game/server/hl2mp/hl2mp_cvars.cpp new file mode 100644 index 0000000..11986ee --- /dev/null +++ b/game/server/hl2mp/hl2mp_cvars.cpp @@ -0,0 +1,25 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "hl2mp_cvars.h" + + + +// Ready restart +ConVar mp_readyrestart( + "mp_readyrestart", + "0", + FCVAR_GAMEDLL, + "If non-zero, game will restart once each player gives the ready signal" ); + +// Ready signal +ConVar mp_ready_signal( + "mp_ready_signal", + "ready", + FCVAR_GAMEDLL, + "Text that each player must speak for the match to begin" );
\ No newline at end of file diff --git a/game/server/hl2mp/hl2mp_cvars.h b/game/server/hl2mp/hl2mp_cvars.h new file mode 100644 index 0000000..4baf52f --- /dev/null +++ b/game/server/hl2mp/hl2mp_cvars.h @@ -0,0 +1,20 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#ifndef HL2MP_CVARS_H +#define HL2MP_CVARS_H +#ifdef _WIN32 +#pragma once +#endif + +#define MAX_INTERMISSION_TIME 120 + +extern ConVar mp_restartround; +extern ConVar mp_readyrestart; +extern ConVar mp_ready_signal; + +#endif //HL2MP_CVARS_H
\ No newline at end of file diff --git a/game/server/hl2mp/hl2mp_gameinterface.cpp b/game/server/hl2mp/hl2mp_gameinterface.cpp new file mode 100644 index 0000000..b5881f7 --- /dev/null +++ b/game/server/hl2mp/hl2mp_gameinterface.cpp @@ -0,0 +1,32 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#include "cbase.h" +#include "gameinterface.h" +#include "mapentities.h" +#include "hl2mp_gameinterface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +// -------------------------------------------------------------------------------------------- // +// Mod-specific CServerGameClients implementation. +// -------------------------------------------------------------------------------------------- // + +void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const +{ + minplayers = defaultMaxPlayers = 2; + maxplayers = 16; +} + +// -------------------------------------------------------------------------------------------- // +// Mod-specific CServerGameDLL implementation. +// -------------------------------------------------------------------------------------------- // + +void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities ) +{ +} + diff --git a/game/server/hl2mp/hl2mp_gameinterface.h b/game/server/hl2mp/hl2mp_gameinterface.h new file mode 100644 index 0000000..4955e46 --- /dev/null +++ b/game/server/hl2mp/hl2mp_gameinterface.h @@ -0,0 +1,10 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +#ifndef HL2MP_GAMEINTERFACE_H +#define HL2MP_GAMEINTERFACE_H +#ifdef _WIN32 +#pragma once +#endif + +#include "gameinterface.h" + +#endif
\ No newline at end of file diff --git a/game/server/hl2mp/hl2mp_player.cpp b/game/server/hl2mp/hl2mp_player.cpp new file mode 100644 index 0000000..1469d84 --- /dev/null +++ b/game/server/hl2mp/hl2mp_player.cpp @@ -0,0 +1,1629 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Player for HL2. +// +//=============================================================================// + +#include "cbase.h" +#include "weapon_hl2mpbasehlmpcombatweapon.h" +#include "hl2mp_player.h" +#include "globalstate.h" +#include "game.h" +#include "gamerules.h" +#include "hl2mp_player_shared.h" +#include "predicted_viewmodel.h" +#include "in_buttons.h" +#include "hl2mp_gamerules.h" +#include "KeyValues.h" +#include "team.h" +#include "weapon_hl2mpbase.h" +#include "grenade_satchel.h" +#include "eventqueue.h" +#include "gamestats.h" + +#include "engine/IEngineSound.h" +#include "SoundEmitterSystem/isoundemittersystembase.h" + +#include "ilagcompensationmanager.h" + +int g_iLastCitizenModel = 0; +int g_iLastCombineModel = 0; + +CBaseEntity *g_pLastCombineSpawn = NULL; +CBaseEntity *g_pLastRebelSpawn = NULL; +extern CBaseEntity *g_pLastSpawn; + +#define HL2MP_COMMAND_MAX_RATE 0.3 + +void DropPrimedFragGrenade( CHL2MP_Player *pPlayer, CBaseCombatWeapon *pGrenade ); + +LINK_ENTITY_TO_CLASS( player, CHL2MP_Player ); + +LINK_ENTITY_TO_CLASS( info_player_combine, CPointEntity ); +LINK_ENTITY_TO_CLASS( info_player_rebel, CPointEntity ); + +IMPLEMENT_SERVERCLASS_ST(CHL2MP_Player, DT_HL2MP_Player) + SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11, SPROP_CHANGES_OFTEN ), + SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11, SPROP_CHANGES_OFTEN ), + SendPropEHandle( SENDINFO( m_hRagdoll ) ), + SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ), + SendPropInt( SENDINFO( m_iPlayerSoundType), 3 ), + + SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ), + SendPropExclude( "DT_BaseFlex", "m_viewtarget" ), + +// SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ), +// SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ), + +END_SEND_TABLE() + +BEGIN_DATADESC( CHL2MP_Player ) +END_DATADESC() + +const char *g_ppszRandomCitizenModels[] = +{ + "models/humans/group03/male_01.mdl", + "models/humans/group03/male_02.mdl", + "models/humans/group03/female_01.mdl", + "models/humans/group03/male_03.mdl", + "models/humans/group03/female_02.mdl", + "models/humans/group03/male_04.mdl", + "models/humans/group03/female_03.mdl", + "models/humans/group03/male_05.mdl", + "models/humans/group03/female_04.mdl", + "models/humans/group03/male_06.mdl", + "models/humans/group03/female_06.mdl", + "models/humans/group03/male_07.mdl", + "models/humans/group03/female_07.mdl", + "models/humans/group03/male_08.mdl", + "models/humans/group03/male_09.mdl", +}; + +const char *g_ppszRandomCombineModels[] = +{ + "models/combine_soldier.mdl", + "models/combine_soldier_prisonguard.mdl", + "models/combine_super_soldier.mdl", + "models/police.mdl", +}; + + +#define MAX_COMBINE_MODELS 4 +#define MODEL_CHANGE_INTERVAL 5.0f +#define TEAM_CHANGE_INTERVAL 5.0f + +#define HL2MPPLAYER_PHYSDAMAGE_SCALE 4.0f + +#pragma warning( disable : 4355 ) + +CHL2MP_Player::CHL2MP_Player() : m_PlayerAnimState( this ) +{ + m_angEyeAngles.Init(); + + m_iLastWeaponFireUsercmd = 0; + + m_flNextModelChangeTime = 0.0f; + m_flNextTeamChangeTime = 0.0f; + + m_iSpawnInterpCounter = 0; + + m_bEnterObserver = false; + m_bReady = false; + + BaseClass::ChangeTeam( 0 ); + +// UseClientSideAnimation(); +} + +CHL2MP_Player::~CHL2MP_Player( void ) +{ + +} + +void CHL2MP_Player::UpdateOnRemove( void ) +{ + if ( m_hRagdoll ) + { + UTIL_RemoveImmediate( m_hRagdoll ); + m_hRagdoll = NULL; + } + + BaseClass::UpdateOnRemove(); +} + +void CHL2MP_Player::Precache( void ) +{ + BaseClass::Precache(); + + PrecacheModel ( "sprites/glow01.vmt" ); + + //Precache Citizen models + int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); + int i; + + for ( i = 0; i < nHeads; ++i ) + PrecacheModel( g_ppszRandomCitizenModels[i] ); + + //Precache Combine Models + nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); + + for ( i = 0; i < nHeads; ++i ) + PrecacheModel( g_ppszRandomCombineModels[i] ); + + PrecacheFootStepSounds(); + + PrecacheScriptSound( "NPC_MetroPolice.Die" ); + PrecacheScriptSound( "NPC_CombineS.Die" ); + PrecacheScriptSound( "NPC_Citizen.die" ); +} + +void CHL2MP_Player::GiveAllItems( void ) +{ + EquipSuit(); + + CBasePlayer::GiveAmmo( 255, "Pistol"); + CBasePlayer::GiveAmmo( 255, "AR2" ); + CBasePlayer::GiveAmmo( 5, "AR2AltFire" ); + CBasePlayer::GiveAmmo( 255, "SMG1"); + CBasePlayer::GiveAmmo( 1, "smg1_grenade"); + CBasePlayer::GiveAmmo( 255, "Buckshot"); + CBasePlayer::GiveAmmo( 32, "357" ); + CBasePlayer::GiveAmmo( 3, "rpg_round"); + + CBasePlayer::GiveAmmo( 1, "grenade" ); + CBasePlayer::GiveAmmo( 2, "slam" ); + + GiveNamedItem( "weapon_crowbar" ); + GiveNamedItem( "weapon_stunstick" ); + GiveNamedItem( "weapon_pistol" ); + GiveNamedItem( "weapon_357" ); + + GiveNamedItem( "weapon_smg1" ); + GiveNamedItem( "weapon_ar2" ); + + GiveNamedItem( "weapon_shotgun" ); + GiveNamedItem( "weapon_frag" ); + + GiveNamedItem( "weapon_crossbow" ); + + GiveNamedItem( "weapon_rpg" ); + + GiveNamedItem( "weapon_slam" ); + + GiveNamedItem( "weapon_physcannon" ); + +} + +void CHL2MP_Player::GiveDefaultItems( void ) +{ + EquipSuit(); + + CBasePlayer::GiveAmmo( 255, "Pistol"); + CBasePlayer::GiveAmmo( 45, "SMG1"); + CBasePlayer::GiveAmmo( 1, "grenade" ); + CBasePlayer::GiveAmmo( 6, "Buckshot"); + CBasePlayer::GiveAmmo( 6, "357" ); + + if ( GetPlayerModelType() == PLAYER_SOUNDS_METROPOLICE || GetPlayerModelType() == PLAYER_SOUNDS_COMBINESOLDIER ) + { + GiveNamedItem( "weapon_stunstick" ); + } + else if ( GetPlayerModelType() == PLAYER_SOUNDS_CITIZEN ) + { + GiveNamedItem( "weapon_crowbar" ); + } + + GiveNamedItem( "weapon_pistol" ); + GiveNamedItem( "weapon_smg1" ); + GiveNamedItem( "weapon_frag" ); + GiveNamedItem( "weapon_physcannon" ); + + const char *szDefaultWeaponName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_defaultweapon" ); + + CBaseCombatWeapon *pDefaultWeapon = Weapon_OwnsThisType( szDefaultWeaponName ); + + if ( pDefaultWeapon ) + { + Weapon_Switch( pDefaultWeapon ); + } + else + { + Weapon_Switch( Weapon_OwnsThisType( "weapon_physcannon" ) ); + } +} + +void CHL2MP_Player::PickDefaultSpawnTeam( void ) +{ + if ( GetTeamNumber() == 0 ) + { + if ( HL2MPRules()->IsTeamplay() == false ) + { + if ( GetModelPtr() == NULL ) + { + const char *szModelName = NULL; + szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); + + if ( ValidatePlayerModel( szModelName ) == false ) + { + char szReturnString[512]; + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel models/combine_soldier.mdl\n" ); + engine->ClientCommand ( edict(), szReturnString ); + } + + ChangeTeam( TEAM_UNASSIGNED ); + } + } + else + { + CTeam *pCombine = g_Teams[TEAM_COMBINE]; + CTeam *pRebels = g_Teams[TEAM_REBELS]; + + if ( pCombine == NULL || pRebels == NULL ) + { + ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); + } + else + { + if ( pCombine->GetNumPlayers() > pRebels->GetNumPlayers() ) + { + ChangeTeam( TEAM_REBELS ); + } + else if ( pCombine->GetNumPlayers() < pRebels->GetNumPlayers() ) + { + ChangeTeam( TEAM_COMBINE ); + } + else + { + ChangeTeam( random->RandomInt( TEAM_COMBINE, TEAM_REBELS ) ); + } + } + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: Sets HL2 specific defaults. +//----------------------------------------------------------------------------- +void CHL2MP_Player::Spawn(void) +{ + m_flNextModelChangeTime = 0.0f; + m_flNextTeamChangeTime = 0.0f; + + PickDefaultSpawnTeam(); + + BaseClass::Spawn(); + + if ( !IsObserver() ) + { + pl.deadflag = false; + RemoveSolidFlags( FSOLID_NOT_SOLID ); + + RemoveEffects( EF_NODRAW ); + + GiveDefaultItems(); + } + + SetNumAnimOverlays( 3 ); + ResetAnimation(); + + m_nRenderFX = kRenderNormal; + + m_Local.m_iHideHUD = 0; + + AddFlag(FL_ONGROUND); // set the player on the ground at the start of the round. + + m_impactEnergyScale = HL2MPPLAYER_PHYSDAMAGE_SCALE; + + if ( HL2MPRules()->IsIntermission() ) + { + AddFlag( FL_FROZEN ); + } + else + { + RemoveFlag( FL_FROZEN ); + } + + m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8; + + m_Local.m_bDucked = false; + + SetPlayerUnderwater(false); + + m_bReady = false; +} + +void CHL2MP_Player::PickupObject( CBaseEntity *pObject, bool bLimitMassAndSize ) +{ + +} + +bool CHL2MP_Player::ValidatePlayerModel( const char *pModel ) +{ + int iModels = ARRAYSIZE( g_ppszRandomCitizenModels ); + int i; + + for ( i = 0; i < iModels; ++i ) + { + if ( !Q_stricmp( g_ppszRandomCitizenModels[i], pModel ) ) + { + return true; + } + } + + iModels = ARRAYSIZE( g_ppszRandomCombineModels ); + + for ( i = 0; i < iModels; ++i ) + { + if ( !Q_stricmp( g_ppszRandomCombineModels[i], pModel ) ) + { + return true; + } + } + + return false; +} + +void CHL2MP_Player::SetPlayerTeamModel( void ) +{ + const char *szModelName = NULL; + szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); + + int modelIndex = modelinfo->GetModelIndex( szModelName ); + + if ( modelIndex == -1 || ValidatePlayerModel( szModelName ) == false ) + { + szModelName = "models/Combine_Soldier.mdl"; + m_iModelType = TEAM_COMBINE; + + char szReturnString[512]; + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); + engine->ClientCommand ( edict(), szReturnString ); + } + + if ( GetTeamNumber() == TEAM_COMBINE ) + { + if ( Q_stristr( szModelName, "models/human") ) + { + int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); + + g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; + szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; + } + + m_iModelType = TEAM_COMBINE; + } + else if ( GetTeamNumber() == TEAM_REBELS ) + { + if ( !Q_stristr( szModelName, "models/human") ) + { + int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); + + g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; + szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; + } + + m_iModelType = TEAM_REBELS; + } + + SetModel( szModelName ); + SetupPlayerSoundsByModel( szModelName ); + + m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; +} + +void CHL2MP_Player::SetPlayerModel( void ) +{ + const char *szModelName = NULL; + const char *pszCurrentModelName = modelinfo->GetModelName( GetModel()); + + szModelName = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ); + + if ( ValidatePlayerModel( szModelName ) == false ) + { + char szReturnString[512]; + + if ( ValidatePlayerModel( pszCurrentModelName ) == false ) + { + pszCurrentModelName = "models/Combine_Soldier.mdl"; + } + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", pszCurrentModelName ); + engine->ClientCommand ( edict(), szReturnString ); + + szModelName = pszCurrentModelName; + } + + if ( GetTeamNumber() == TEAM_COMBINE ) + { + int nHeads = ARRAYSIZE( g_ppszRandomCombineModels ); + + g_iLastCombineModel = ( g_iLastCombineModel + 1 ) % nHeads; + szModelName = g_ppszRandomCombineModels[g_iLastCombineModel]; + + m_iModelType = TEAM_COMBINE; + } + else if ( GetTeamNumber() == TEAM_REBELS ) + { + int nHeads = ARRAYSIZE( g_ppszRandomCitizenModels ); + + g_iLastCitizenModel = ( g_iLastCitizenModel + 1 ) % nHeads; + szModelName = g_ppszRandomCitizenModels[g_iLastCitizenModel]; + + m_iModelType = TEAM_REBELS; + } + else + { + if ( Q_strlen( szModelName ) == 0 ) + { + szModelName = g_ppszRandomCitizenModels[0]; + } + + if ( Q_stristr( szModelName, "models/human") ) + { + m_iModelType = TEAM_REBELS; + } + else + { + m_iModelType = TEAM_COMBINE; + } + } + + int modelIndex = modelinfo->GetModelIndex( szModelName ); + + if ( modelIndex == -1 ) + { + szModelName = "models/Combine_Soldier.mdl"; + m_iModelType = TEAM_COMBINE; + + char szReturnString[512]; + + Q_snprintf( szReturnString, sizeof (szReturnString ), "cl_playermodel %s\n", szModelName ); + engine->ClientCommand ( edict(), szReturnString ); + } + + SetModel( szModelName ); + SetupPlayerSoundsByModel( szModelName ); + + m_flNextModelChangeTime = gpGlobals->curtime + MODEL_CHANGE_INTERVAL; +} + +void CHL2MP_Player::SetupPlayerSoundsByModel( const char *pModelName ) +{ + if ( Q_stristr( pModelName, "models/human") ) + { + m_iPlayerSoundType = (int)PLAYER_SOUNDS_CITIZEN; + } + else if ( Q_stristr(pModelName, "police" ) ) + { + m_iPlayerSoundType = (int)PLAYER_SOUNDS_METROPOLICE; + } + else if ( Q_stristr(pModelName, "combine" ) ) + { + m_iPlayerSoundType = (int)PLAYER_SOUNDS_COMBINESOLDIER; + } +} + +void CHL2MP_Player::ResetAnimation( void ) +{ + if ( IsAlive() ) + { + SetSequence ( -1 ); + SetActivity( ACT_INVALID ); + + if (!GetAbsVelocity().x && !GetAbsVelocity().y) + SetAnimation( PLAYER_IDLE ); + else if ((GetAbsVelocity().x || GetAbsVelocity().y) && ( GetFlags() & FL_ONGROUND )) + SetAnimation( PLAYER_WALK ); + else if (GetWaterLevel() > 1) + SetAnimation( PLAYER_WALK ); + } +} + + +bool CHL2MP_Player::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex ) +{ + bool bRet = BaseClass::Weapon_Switch( pWeapon, viewmodelindex ); + + if ( bRet == true ) + { + ResetAnimation(); + } + + return bRet; +} + +void CHL2MP_Player::PreThink( void ) +{ + QAngle vOldAngles = GetLocalAngles(); + QAngle vTempAngles = GetLocalAngles(); + + vTempAngles = EyeAngles(); + + if ( vTempAngles[PITCH] > 180.0f ) + { + vTempAngles[PITCH] -= 360.0f; + } + + SetLocalAngles( vTempAngles ); + + BaseClass::PreThink(); + State_PreThink(); + + //Reset bullet force accumulator, only lasts one frame + m_vecTotalBulletForce = vec3_origin; + SetLocalAngles( vOldAngles ); +} + +void CHL2MP_Player::PostThink( void ) +{ + BaseClass::PostThink(); + + if ( GetFlags() & FL_DUCKING ) + { + SetCollisionBounds( VEC_CROUCH_TRACE_MIN, VEC_CROUCH_TRACE_MAX ); + } + + m_PlayerAnimState.Update(); + + // Store the eye angles pitch so the client can compute its animation state correctly. + m_angEyeAngles = EyeAngles(); + + QAngle angles = GetLocalAngles(); + angles[PITCH] = 0; + SetLocalAngles( angles ); +} + +void CHL2MP_Player::PlayerDeathThink() +{ + if( !IsObserver() ) + { + BaseClass::PlayerDeathThink(); + } +} + +void CHL2MP_Player::FireBullets ( const FireBulletsInfo_t &info ) +{ + // Move other players back to history positions based on local player's lag + lagcompensation->StartLagCompensation( this, this->GetCurrentCommand() ); + + FireBulletsInfo_t modinfo = info; + + CWeaponHL2MPBase *pWeapon = dynamic_cast<CWeaponHL2MPBase *>( GetActiveWeapon() ); + + if ( pWeapon ) + { + modinfo.m_iPlayerDamage = modinfo.m_flDamage = pWeapon->GetHL2MPWpnData().m_iPlayerDamage; + } + + NoteWeaponFired(); + + BaseClass::FireBullets( modinfo ); + + // Move other players back to history positions based on local player's lag + lagcompensation->FinishLagCompensation( this ); +} + +void CHL2MP_Player::NoteWeaponFired( void ) +{ + Assert( m_pCurrentCommand ); + if( m_pCurrentCommand ) + { + m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number; + } +} + +extern ConVar sv_maxunlag; + +bool CHL2MP_Player::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) ) + return false; + + // If this entity hasn't been transmitted to us and acked, then don't bother lag compensating it. + if ( pEntityTransmitBits && !pEntityTransmitBits->Get( pPlayer->entindex() ) ) + return false; + + const Vector &vMyOrigin = GetAbsOrigin(); + const Vector &vHisOrigin = pPlayer->GetAbsOrigin(); + + // get max distance player could have moved within max lag compensation time, + // multiply by 1.5 to to avoid "dead zones" (sqrt(2) would be the exact value) + float maxDistance = 1.5 * pPlayer->MaxSpeed() * sv_maxunlag.GetFloat(); + + // If the player is within this distance, lag compensate them in case they're running past us. + if ( vHisOrigin.DistTo( vMyOrigin ) < maxDistance ) + return true; + + // If their origin is not within a 45 degree cone in front of us, no need to lag compensate. + Vector vForward; + AngleVectors( pCmd->viewangles, &vForward ); + + Vector vDiff = vHisOrigin - vMyOrigin; + VectorNormalize( vDiff ); + + float flCosAngle = 0.707107f; // 45 degree angle + if ( vForward.Dot( vDiff ) < flCosAngle ) + return false; + + return true; +} + +Activity CHL2MP_Player::TranslateTeamActivity( Activity ActToTranslate ) +{ + if ( m_iModelType == TEAM_COMBINE ) + return ActToTranslate; + + if ( ActToTranslate == ACT_RUN ) + return ACT_RUN_AIM_AGITATED; + + if ( ActToTranslate == ACT_IDLE ) + return ACT_IDLE_AIM_AGITATED; + + if ( ActToTranslate == ACT_WALK ) + return ACT_WALK_AIM_AGITATED; + + return ActToTranslate; +} + +extern ConVar hl2_normspeed; + +// Set the activity based on an event or current state +void CHL2MP_Player::SetAnimation( PLAYER_ANIM playerAnim ) +{ + int animDesired; + + float speed; + + speed = GetAbsVelocity().Length2D(); + + + // bool bRunning = true; + + //Revisit! +/* if ( ( m_nButtons & ( IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT ) ) ) + { + if ( speed > 1.0f && speed < hl2_normspeed.GetFloat() - 20.0f ) + { + bRunning = false; + } + }*/ + + if ( GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) ) + { + speed = 0; + playerAnim = PLAYER_IDLE; + } + + Activity idealActivity = ACT_HL2MP_RUN; + + // This could stand to be redone. Why is playerAnim abstracted from activity? (sjb) + if ( playerAnim == PLAYER_JUMP ) + { + idealActivity = ACT_HL2MP_JUMP; + } + else if ( playerAnim == PLAYER_DIE ) + { + if ( m_lifeState == LIFE_ALIVE ) + { + return; + } + } + else if ( playerAnim == PLAYER_ATTACK1 ) + { + if ( GetActivity( ) == ACT_HOVER || + GetActivity( ) == ACT_SWIM || + GetActivity( ) == ACT_HOP || + GetActivity( ) == ACT_LEAP || + GetActivity( ) == ACT_DIESIMPLE ) + { + idealActivity = GetActivity( ); + } + else + { + idealActivity = ACT_HL2MP_GESTURE_RANGE_ATTACK; + } + } + else if ( playerAnim == PLAYER_RELOAD ) + { + idealActivity = ACT_HL2MP_GESTURE_RELOAD; + } + else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK ) + { + if ( !( GetFlags() & FL_ONGROUND ) && GetActivity( ) == ACT_HL2MP_JUMP ) // Still jumping + { + idealActivity = GetActivity( ); + } + /* + else if ( GetWaterLevel() > 1 ) + { + if ( speed == 0 ) + idealActivity = ACT_HOVER; + else + idealActivity = ACT_SWIM; + } + */ + else + { + if ( GetFlags() & FL_DUCKING ) + { + if ( speed > 0 ) + { + idealActivity = ACT_HL2MP_WALK_CROUCH; + } + else + { + idealActivity = ACT_HL2MP_IDLE_CROUCH; + } + } + else + { + if ( speed > 0 ) + { + /* + if ( bRunning == false ) + { + idealActivity = ACT_WALK; + } + else + */ + { + idealActivity = ACT_HL2MP_RUN; + } + } + else + { + idealActivity = ACT_HL2MP_IDLE; + } + } + } + + idealActivity = TranslateTeamActivity( idealActivity ); + } + + if ( idealActivity == ACT_HL2MP_GESTURE_RANGE_ATTACK ) + { + RestartGesture( Weapon_TranslateActivity( idealActivity ) ); + + // FIXME: this seems a bit wacked + Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 ); + + return; + } + else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD ) + { + RestartGesture( Weapon_TranslateActivity( idealActivity ) ); + return; + } + else + { + SetActivity( idealActivity ); + + animDesired = SelectWeightedSequence( Weapon_TranslateActivity ( idealActivity ) ); + + if (animDesired == -1) + { + animDesired = SelectWeightedSequence( idealActivity ); + + if ( animDesired == -1 ) + { + animDesired = 0; + } + } + + // Already using the desired animation? + if ( GetSequence() == animDesired ) + return; + + m_flPlaybackRate = 1.0; + ResetSequence( animDesired ); + SetCycle( 0 ); + return; + } + + // Already using the desired animation? + if ( GetSequence() == animDesired ) + return; + + //Msg( "Set animation to %d\n", animDesired ); + // Reset to first frame of desired animation + ResetSequence( animDesired ); + SetCycle( 0 ); +} + + +extern int gEvilImpulse101; +//----------------------------------------------------------------------------- +// Purpose: Player reacts to bumping a weapon. +// Input : pWeapon - the weapon that the player bumped into. +// Output : Returns true if player picked up the weapon +//----------------------------------------------------------------------------- +bool CHL2MP_Player::BumpWeapon( CBaseCombatWeapon *pWeapon ) +{ + CBaseCombatCharacter *pOwner = pWeapon->GetOwner(); + + // Can I have this weapon type? + if ( !IsAllowedToPickupWeapons() ) + return false; + + if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) ) + { + if ( gEvilImpulse101 ) + { + UTIL_Remove( pWeapon ); + } + return false; + } + + // Don't let the player fetch weapons through walls (use MASK_SOLID so that you can't pickup through windows) + if( !pWeapon->FVisible( this, MASK_SOLID ) && !(GetFlags() & FL_NOTARGET) ) + { + return false; + } + + bool bOwnsWeaponAlready = !!Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()); + + if ( bOwnsWeaponAlready == true ) + { + //If we have room for the ammo, then "take" the weapon too. + if ( Weapon_EquipAmmoOnly( pWeapon ) ) + { + pWeapon->CheckRespawn(); + + UTIL_Remove( pWeapon ); + return true; + } + else + { + return false; + } + } + + pWeapon->CheckRespawn(); + Weapon_Equip( pWeapon ); + + return true; +} + +void CHL2MP_Player::ChangeTeam( int iTeam ) +{ +/* if ( GetNextTeamChangeTime() >= gpGlobals->curtime ) + { + char szReturnString[128]; + Q_snprintf( szReturnString, sizeof( szReturnString ), "Please wait %d more seconds before trying to switch teams again.\n", (int)(GetNextTeamChangeTime() - gpGlobals->curtime) ); + + ClientPrint( this, HUD_PRINTTALK, szReturnString ); + return; + }*/ + + bool bKill = false; + + if ( HL2MPRules()->IsTeamplay() != true && iTeam != TEAM_SPECTATOR ) + { + //don't let them try to join combine or rebels during deathmatch. + iTeam = TEAM_UNASSIGNED; + } + + if ( HL2MPRules()->IsTeamplay() == true ) + { + if ( iTeam != GetTeamNumber() && GetTeamNumber() != TEAM_UNASSIGNED ) + { + bKill = true; + } + } + + BaseClass::ChangeTeam( iTeam ); + + m_flNextTeamChangeTime = gpGlobals->curtime + TEAM_CHANGE_INTERVAL; + + if ( HL2MPRules()->IsTeamplay() == true ) + { + SetPlayerTeamModel(); + } + else + { + SetPlayerModel(); + } + + if ( iTeam == TEAM_SPECTATOR ) + { + RemoveAllItems( true ); + + State_Transition( STATE_OBSERVER_MODE ); + } + + if ( bKill == true ) + { + CommitSuicide(); + } +} + +bool CHL2MP_Player::HandleCommand_JoinTeam( int team ) +{ + if ( !GetGlobalTeam( team ) || team == 0 ) + { + Warning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team ); + return false; + } + + if ( team == TEAM_SPECTATOR ) + { + // Prevent this is the cvar is set + if ( !mp_allowspectators.GetInt() ) + { + ClientPrint( this, HUD_PRINTCENTER, "#Cannot_Be_Spectator" ); + return false; + } + + if ( GetTeamNumber() != TEAM_UNASSIGNED && !IsDead() ) + { + 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 ); + + return true; + } + else + { + StopObserverMode(); + State_Transition(STATE_ACTIVE); + } + + // Switch their actual team... + ChangeTeam( team ); + + return true; +} + +bool CHL2MP_Player::ClientCommand( const CCommand &args ) +{ + if ( FStrEq( args[0], "spectate" ) ) + { + if ( ShouldRunRateLimitedCommand( args ) ) + { + // instantly join spectators + HandleCommand_JoinTeam( TEAM_SPECTATOR ); + } + return true; + } + else if ( FStrEq( args[0], "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( args[0], "joingame" ) ) + { + return true; + } + + return BaseClass::ClientCommand( args ); +} + +void CHL2MP_Player::CheatImpulseCommands( int iImpulse ) +{ + switch ( iImpulse ) + { + case 101: + { + if( sv_cheats->GetBool() ) + { + GiveAllItems(); + } + } + break; + + default: + BaseClass::CheatImpulseCommands( iImpulse ); + } +} + +bool CHL2MP_Player::ShouldRunRateLimitedCommand( const CCommand &args ) +{ + int i = m_RateLimitLastCommandTimes.Find( args[0] ); + if ( i == m_RateLimitLastCommandTimes.InvalidIndex() ) + { + m_RateLimitLastCommandTimes.Insert( args[0], gpGlobals->curtime ); + return true; + } + else if ( (gpGlobals->curtime - m_RateLimitLastCommandTimes[i]) < HL2MP_COMMAND_MAX_RATE ) + { + // Too fast. + return false; + } + else + { + m_RateLimitLastCommandTimes[i] = gpGlobals->curtime; + return true; + } +} + +void CHL2MP_Player::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 CHL2MP_Player::BecomeRagdollOnClient( const Vector &force ) +{ + return true; +} + +// -------------------------------------------------------------------------------- // +// Ragdoll entities. +// -------------------------------------------------------------------------------- // + +class CHL2MPRagdoll : public CBaseAnimatingOverlay +{ +public: + DECLARE_CLASS( CHL2MPRagdoll, CBaseAnimatingOverlay ); + DECLARE_SERVERCLASS(); + + // Transmit ragdolls to everyone. + virtual int UpdateTransmitState() + { + return SetTransmitState( FL_EDICT_ALWAYS ); + } + +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 ); +}; + +LINK_ENTITY_TO_CLASS( hl2mp_ragdoll, CHL2MPRagdoll ); + +IMPLEMENT_SERVERCLASS_ST_NOBASE( CHL2MPRagdoll, DT_HL2MPRagdoll ) + 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 ) ) +END_SEND_TABLE() + + +void CHL2MP_Player::CreateRagdollEntity( void ) +{ + if ( m_hRagdoll ) + { + UTIL_RemoveImmediate( m_hRagdoll ); + m_hRagdoll = NULL; + } + + // If we already have a ragdoll, don't make another one. + CHL2MPRagdoll *pRagdoll = dynamic_cast< CHL2MPRagdoll* >( m_hRagdoll.Get() ); + + if ( !pRagdoll ) + { + // create a new one + pRagdoll = dynamic_cast< CHL2MPRagdoll* >( CreateEntityByName( "hl2mp_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->SetAbsOrigin( GetAbsOrigin() ); + } + + // ragdolls will be removed on round restart automatically + m_hRagdoll = pRagdoll; +} + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +int CHL2MP_Player::FlashlightIsOn( void ) +{ + return IsEffectActive( EF_DIMLIGHT ); +} + +extern ConVar flashlight; + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2MP_Player::FlashlightTurnOn( void ) +{ + if( flashlight.GetInt() > 0 && IsAlive() ) + { + AddEffects( EF_DIMLIGHT ); + EmitSound( "HL2Player.FlashlightOn" ); + } +} + + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void CHL2MP_Player::FlashlightTurnOff( void ) +{ + RemoveEffects( EF_DIMLIGHT ); + + if( IsAlive() ) + { + EmitSound( "HL2Player.FlashlightOff" ); + } +} + +void CHL2MP_Player::Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget, const Vector *pVelocity ) +{ + //Drop a grenade if it's primed. + if ( GetActiveWeapon() ) + { + CBaseCombatWeapon *pGrenade = Weapon_OwnsThisType("weapon_frag"); + + if ( GetActiveWeapon() == pGrenade ) + { + if ( ( m_nButtons & IN_ATTACK ) || (m_nButtons & IN_ATTACK2) ) + { + DropPrimedFragGrenade( this, pGrenade ); + return; + } + } + } + + BaseClass::Weapon_Drop( pWeapon, pvecTarget, pVelocity ); +} + + +void CHL2MP_Player::DetonateTripmines( void ) +{ + CBaseEntity *pEntity = NULL; + + while ((pEntity = gEntList.FindEntityByClassname( pEntity, "npc_satchel" )) != NULL) + { + CSatchelCharge *pSatchel = dynamic_cast<CSatchelCharge *>(pEntity); + if (pSatchel->m_bIsLive && pSatchel->GetThrower() == this ) + { + g_EventQueue.AddEvent( pSatchel, "Explode", 0.20, this, this ); + } + } + + // Play sound for pressing the detonator + EmitSound( "Weapon_SLAM.SatchelDetonate" ); +} + +void CHL2MP_Player::Event_Killed( const CTakeDamageInfo &info ) +{ + //update damage info with our accumulated physics force + CTakeDamageInfo subinfo = info; + subinfo.SetDamageForce( m_vecTotalBulletForce ); + + SetNumAnimOverlays( 0 ); + + // 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(); + + DetonateTripmines(); + + BaseClass::Event_Killed( subinfo ); + + if ( info.GetDamageType() & DMG_DISSOLVE ) + { + if ( m_hRagdoll ) + { + m_hRagdoll->GetBaseAnimating()->Dissolve( NULL, gpGlobals->curtime, false, ENTITY_DISSOLVE_NORMAL ); + } + } + + CBaseEntity *pAttacker = info.GetAttacker(); + + if ( pAttacker ) + { + int iScoreToAdd = 1; + + if ( pAttacker == this ) + { + iScoreToAdd = -1; + } + + GetGlobalTeam( pAttacker->GetTeamNumber() )->AddScore( iScoreToAdd ); + } + + FlashlightTurnOff(); + + m_lifeState = LIFE_DEAD; + + RemoveEffects( EF_NODRAW ); // still draw player body + StopZooming(); +} + +int CHL2MP_Player::OnTakeDamage( const CTakeDamageInfo &inputInfo ) +{ + //return here if the player is in the respawn grace period vs. slams. + if ( gpGlobals->curtime < m_flSlamProtectTime && (inputInfo.GetDamageType() == DMG_BLAST ) ) + return 0; + + m_vecTotalBulletForce += inputInfo.GetDamageForce(); + + gamestats->Event_PlayerDamage( this, inputInfo ); + + return BaseClass::OnTakeDamage( inputInfo ); +} + +void CHL2MP_Player::DeathSound( const CTakeDamageInfo &info ) +{ + if ( m_hRagdoll && m_hRagdoll->GetBaseAnimating()->IsDissolving() ) + return; + + char szStepSound[128]; + + Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.Die", GetPlayerModelSoundPrefix() ); + + const char *pModelName = STRING( GetModelName() ); + + CSoundParameters params; + if ( GetParametersForSound( szStepSound, params, pModelName ) == false ) + return; + + Vector vecOrigin = GetAbsOrigin(); + + CRecipientFilter filter; + filter.AddRecipientsByPAS( vecOrigin ); + + EmitSound_t ep; + ep.m_nChannel = params.channel; + ep.m_pSoundName = params.soundname; + ep.m_flVolume = params.volume; + ep.m_SoundLevel = params.soundlevel; + ep.m_nFlags = 0; + ep.m_nPitch = params.pitch; + ep.m_pOrigin = &vecOrigin; + + EmitSound( filter, entindex(), ep ); +} + +CBaseEntity* CHL2MP_Player::EntSelectSpawnPoint( void ) +{ + CBaseEntity *pSpot = NULL; + CBaseEntity *pLastSpawnPoint = g_pLastSpawn; + edict_t *player = edict(); + const char *pSpawnpointName = "info_player_deathmatch"; + + if ( HL2MPRules()->IsTeamplay() == true ) + { + if ( GetTeamNumber() == TEAM_COMBINE ) + { + pSpawnpointName = "info_player_combine"; + pLastSpawnPoint = g_pLastCombineSpawn; + } + else if ( GetTeamNumber() == TEAM_REBELS ) + { + pSpawnpointName = "info_player_rebel"; + pLastSpawnPoint = g_pLastRebelSpawn; + } + + if ( gEntList.FindEntityByClassname( NULL, pSpawnpointName ) == NULL ) + { + pSpawnpointName = "info_player_deathmatch"; + pLastSpawnPoint = g_pLastSpawn; + } + } + + pSpot = pLastSpawnPoint; + // Randomize the start spot + for ( int i = random->RandomInt(1,5); i > 0; i-- ) + pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); + if ( !pSpot ) // skip over the null point + pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); + + CBaseEntity *pFirstSpot = pSpot; + + do + { + if ( pSpot ) + { + // check if pSpot is valid + if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) ) + { + if ( pSpot->GetLocalOrigin() == vec3_origin ) + { + pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); + continue; + } + + // if so, go to pSpot + goto ReturnSpot; + } + } + // increment pSpot + pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); + } while ( pSpot != pFirstSpot ); // loop if we're not back to the start + + // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there + if ( pSpot ) + { + CBaseEntity *ent = NULL; + for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) + { + // if ent is a client, kill em (unless they are ourselves) + if ( ent->IsPlayer() && !(ent->edict() == player) ) + ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) ); + } + goto ReturnSpot; + } + + if ( !pSpot ) + { + pSpot = gEntList.FindEntityByClassname( pSpot, "info_player_start" ); + + if ( pSpot ) + goto ReturnSpot; + } + +ReturnSpot: + + if ( HL2MPRules()->IsTeamplay() == true ) + { + if ( GetTeamNumber() == TEAM_COMBINE ) + { + g_pLastCombineSpawn = pSpot; + } + else if ( GetTeamNumber() == TEAM_REBELS ) + { + g_pLastRebelSpawn = pSpot; + } + } + + g_pLastSpawn = pSpot; + + m_flSlamProtectTime = gpGlobals->curtime + 0.5; + + return pSpot; +} + + +CON_COMMAND( timeleft, "prints the time remaining in the match" ) +{ + CHL2MP_Player *pPlayer = ToHL2MPPlayer( UTIL_GetCommandClient() ); + + int iTimeRemaining = (int)HL2MPRules()->GetMapRemainingTime(); + + if ( iTimeRemaining == 0 ) + { + if ( pPlayer ) + { + ClientPrint( pPlayer, HUD_PRINTTALK, "This game has no timelimit." ); + } + else + { + Msg( "* No Time Limit *\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, "Time left in map: %s1:%s2", minutes, seconds ); + } + else + { + Msg( "Time Remaining: %s:%s\n", minutes, seconds ); + } + } +} + + +void CHL2MP_Player::Reset() +{ + ResetDeathCount(); + ResetFragCount(); +} + +bool CHL2MP_Player::IsReady() +{ + return m_bReady; +} + +void CHL2MP_Player::SetReady( bool bReady ) +{ + m_bReady = bReady; +} + +void CHL2MP_Player::CheckChatText( char *p, int bufsize ) +{ + //Look for escape sequences and replace + + char *buf = new char[bufsize]; + int pos = 0; + + // Parse say text for escape sequences + for ( char *pSrc = p; pSrc != NULL && *pSrc != 0 && pos < bufsize-1; pSrc++ ) + { + // copy each char across + buf[pos] = *pSrc; + pos++; + } + + buf[pos] = '\0'; + + // copy buf back into p + Q_strncpy( p, buf, bufsize ); + + delete[] buf; + + const char *pReadyCheck = p; + + HL2MPRules()->CheckChatForReadySignal( this, pReadyCheck ); +} + +void CHL2MP_Player::State_Transition( HL2MPPlayerState newState ) +{ + State_Leave(); + State_Enter( newState ); +} + + +void CHL2MP_Player::State_Enter( HL2MPPlayerState newState ) +{ + m_iPlayerState = newState; + m_pCurStateInfo = State_LookupInfo( newState ); + + // Initialize the new state. + if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState ) + (this->*m_pCurStateInfo->pfnEnterState)(); +} + + +void CHL2MP_Player::State_Leave() +{ + if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState ) + { + (this->*m_pCurStateInfo->pfnLeaveState)(); + } +} + + +void CHL2MP_Player::State_PreThink() +{ + if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink ) + { + (this->*m_pCurStateInfo->pfnPreThink)(); + } +} + + +CHL2MPPlayerStateInfo *CHL2MP_Player::State_LookupInfo( HL2MPPlayerState state ) +{ + // This table MUST match the + static CHL2MPPlayerStateInfo playerStateInfos[] = + { + { STATE_ACTIVE, "STATE_ACTIVE", &CHL2MP_Player::State_Enter_ACTIVE, NULL, &CHL2MP_Player::State_PreThink_ACTIVE }, + { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CHL2MP_Player::State_Enter_OBSERVER_MODE, NULL, &CHL2MP_Player::State_PreThink_OBSERVER_MODE } + }; + + for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ ) + { + if ( playerStateInfos[i].m_iPlayerState == state ) + return &playerStateInfos[i]; + } + + return NULL; +} + +bool CHL2MP_Player::StartObserverMode(int mode) +{ + //we only want to go into observer mode if the player asked to, not on a death timeout + if ( m_bEnterObserver == true ) + { + VPhysicsDestroyObject(); + return BaseClass::StartObserverMode( mode ); + } + return false; +} + +void CHL2MP_Player::StopObserverMode() +{ + m_bEnterObserver = false; + BaseClass::StopObserverMode(); +} + +void CHL2MP_Player::State_Enter_OBSERVER_MODE() +{ + int observerMode = m_iObserverLastMode; + if ( IsNetClient() ) + { + const char *pIdealMode = engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_spec_mode" ); + if ( pIdealMode ) + { + observerMode = atoi( pIdealMode ); + if ( observerMode <= OBS_MODE_FIXED || observerMode > OBS_MODE_ROAMING ) + { + observerMode = m_iObserverLastMode; + } + } + } + m_bEnterObserver = true; + StartObserverMode( observerMode ); +} + +void CHL2MP_Player::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 CHL2MP_Player::State_Enter_ACTIVE() +{ + SetMoveType( MOVETYPE_WALK ); + + // md 8/15/07 - They'll get set back to solid when they actually respawn. If we set them solid now and mp_forcerespawn + // is false, then they'll be spectating but blocking live players from moving. + // RemoveSolidFlags( FSOLID_NOT_SOLID ); + + m_Local.m_iHideHUD = 0; +} + + +void CHL2MP_Player::State_PreThink_ACTIVE() +{ + //we don't really need to do anything here. + //This state_prethink structure came over from CS:S and was doing an assert check that fails the way hl2dm handles death +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CHL2MP_Player::CanHearAndReadChatFrom( CBasePlayer *pPlayer ) +{ + // can always hear the console unless we're ignoring all chat + if ( !pPlayer ) + return false; + + return true; +} diff --git a/game/server/hl2mp/hl2mp_player.h b/game/server/hl2mp/hl2mp_player.h new file mode 100644 index 0000000..14dcda6 --- /dev/null +++ b/game/server/hl2mp/hl2mp_player.h @@ -0,0 +1,176 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +// +//=============================================================================// +#ifndef HL2MP_PLAYER_H +#define HL2MP_PLAYER_H +#pragma once + +class CHL2MP_Player; + +#include "basemultiplayerplayer.h" +#include "hl2_playerlocaldata.h" +#include "hl2_player.h" +#include "simtimer.h" +#include "soundenvelope.h" +#include "hl2mp_player_shared.h" +#include "hl2mp_gamerules.h" +#include "utldict.h" + +//============================================================================= +// >> HL2MP_Player +//============================================================================= +class CHL2MPPlayerStateInfo +{ +public: + HL2MPPlayerState m_iPlayerState; + const char *m_pStateName; + + void (CHL2MP_Player::*pfnEnterState)(); // Init and deinit the state. + void (CHL2MP_Player::*pfnLeaveState)(); + + void (CHL2MP_Player::*pfnPreThink)(); // Do a PreThink() in this state. +}; + +class CHL2MP_Player : public CHL2_Player +{ +public: + DECLARE_CLASS( CHL2MP_Player, CHL2_Player ); + + CHL2MP_Player(); + ~CHL2MP_Player( void ); + + static CHL2MP_Player *CreatePlayer( const char *className, edict_t *ed ) + { + CHL2MP_Player::s_PlayerEdict = ed; + return (CHL2MP_Player*)CreateEntityByName( className ); + } + + DECLARE_SERVERCLASS(); + DECLARE_DATADESC(); + + virtual void Precache( void ); + virtual void Spawn( void ); + virtual void PostThink( void ); + virtual void PreThink( void ); + virtual void PlayerDeathThink( void ); + virtual void SetAnimation( PLAYER_ANIM playerAnim ); + virtual bool HandleCommand_JoinTeam( int team ); + virtual bool ClientCommand( const CCommand &args ); + virtual void CreateViewModel( int viewmodelindex = 0 ); + virtual bool BecomeRagdollOnClient( const Vector &force ); + virtual void Event_Killed( const CTakeDamageInfo &info ); + virtual int OnTakeDamage( const CTakeDamageInfo &inputInfo ); + virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const; + virtual void FireBullets ( const FireBulletsInfo_t &info ); + virtual bool Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex = 0); + virtual bool BumpWeapon( CBaseCombatWeapon *pWeapon ); + virtual void ChangeTeam( int iTeam ) OVERRIDE; + virtual void PickupObject ( CBaseEntity *pObject, bool bLimitMassAndSize ); + virtual void PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ); + virtual void Weapon_Drop( CBaseCombatWeapon *pWeapon, const Vector *pvecTarget = NULL, const Vector *pVelocity = NULL ); + virtual void UpdateOnRemove( void ); + virtual void DeathSound( const CTakeDamageInfo &info ); + virtual CBaseEntity* EntSelectSpawnPoint( void ); + + int FlashlightIsOn( void ); + void FlashlightTurnOn( void ); + void FlashlightTurnOff( void ); + void PrecacheFootStepSounds( void ); + bool ValidatePlayerModel( const char *pModel ); + + QAngle GetAnimEyeAngles( void ) { return m_angEyeAngles.Get(); } + + Vector GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget = NULL ); + + void CheatImpulseCommands( int iImpulse ); + void CreateRagdollEntity( void ); + void GiveAllItems( void ); + void GiveDefaultItems( void ); + + void NoteWeaponFired( void ); + + void ResetAnimation( void ); + void SetPlayerModel( void ); + void SetPlayerTeamModel( void ); + Activity TranslateTeamActivity( Activity ActToTranslate ); + + float GetNextModelChangeTime( void ) { return m_flNextModelChangeTime; } + float GetNextTeamChangeTime( void ) { return m_flNextTeamChangeTime; } + void PickDefaultSpawnTeam( void ); + void SetupPlayerSoundsByModel( const char *pModelName ); + const char *GetPlayerModelSoundPrefix( void ); + int GetPlayerModelType( void ) { return m_iPlayerSoundType; } + + void DetonateTripmines( void ); + + void Reset(); + + bool IsReady(); + void SetReady( bool bReady ); + + void CheckChatText( char *p, int bufsize ); + + void State_Transition( HL2MPPlayerState newState ); + void State_Enter( HL2MPPlayerState newState ); + void State_Leave(); + void State_PreThink(); + CHL2MPPlayerStateInfo *State_LookupInfo( HL2MPPlayerState state ); + + void State_Enter_ACTIVE(); + void State_PreThink_ACTIVE(); + void State_Enter_OBSERVER_MODE(); + void State_PreThink_OBSERVER_MODE(); + + + virtual bool StartObserverMode( int mode ); + virtual void StopObserverMode( void ); + + + Vector m_vecTotalBulletForce; //Accumulator for bullet force in a single frame + + // Tracks our ragdoll entity. + CNetworkHandle( CBaseEntity, m_hRagdoll ); // networked entity handle + + virtual bool CanHearAndReadChatFrom( CBasePlayer *pPlayer ); + + +private: + + CNetworkQAngle( m_angEyeAngles ); + CPlayerAnimState m_PlayerAnimState; + + int m_iLastWeaponFireUsercmd; + int m_iModelType; + CNetworkVar( int, m_iSpawnInterpCounter ); + CNetworkVar( int, m_iPlayerSoundType ); + + float m_flNextModelChangeTime; + float m_flNextTeamChangeTime; + + float m_flSlamProtectTime; + + HL2MPPlayerState m_iPlayerState; + CHL2MPPlayerStateInfo *m_pCurStateInfo; + + bool ShouldRunRateLimitedCommand( const CCommand &args ); + + // This lets us rate limit the commands the players can execute so they don't overflow things like reliable buffers. + CUtlDict<float,int> m_RateLimitLastCommandTimes; + + bool m_bEnterObserver; + bool m_bReady; +}; + +inline CHL2MP_Player *ToHL2MPPlayer( CBaseEntity *pEntity ) +{ + if ( !pEntity || !pEntity->IsPlayer() ) + return NULL; + + return dynamic_cast<CHL2MP_Player*>( pEntity ); +} + +#endif //HL2MP_PLAYER_H diff --git a/game/server/hl2mp/te_hl2mp_shotgun_shot.cpp b/game/server/hl2mp/te_hl2mp_shotgun_shot.cpp new file mode 100644 index 0000000..6c93776 --- /dev/null +++ b/game/server/hl2mp/te_hl2mp_shotgun_shot.cpp @@ -0,0 +1,104 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Workfile: $ +// $Date: $ +// +//----------------------------------------------------------------------------- +// $Log: $ +// +// $NoKeywords: $ +//=============================================================================// +#include "cbase.h" +#include "basetempentity.h" + + +#define NUM_BULLET_SEED_BITS 8 + + +//----------------------------------------------------------------------------- +// Purpose: Display's a blood sprite +//----------------------------------------------------------------------------- +class CTEHL2MPFireBullets : public CBaseTempEntity +{ +public: + DECLARE_CLASS( CTEHL2MPFireBullets, CBaseTempEntity ); + DECLARE_SERVERCLASS(); + + CTEHL2MPFireBullets( const char *name ); + virtual ~CTEHL2MPFireBullets( void ); + +public: + CNetworkVar( int, m_iPlayer ); + CNetworkVector( m_vecOrigin ); + CNetworkVector( m_vecDir ); + CNetworkVar( int, m_iAmmoID ); + CNetworkVar( int, m_iSeed ); + CNetworkVar( int, m_iShots ); + CNetworkVar( float, m_flSpread ); + CNetworkVar( bool, m_bDoImpacts ); + CNetworkVar( bool, m_bDoTracers ); +}; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : *name - +//----------------------------------------------------------------------------- +CTEHL2MPFireBullets::CTEHL2MPFireBullets( const char *name ) : + CBaseTempEntity( name ) +{ +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CTEHL2MPFireBullets::~CTEHL2MPFireBullets( void ) +{ +} + +IMPLEMENT_SERVERCLASS_ST_NOBASE(CTEHL2MPFireBullets, DT_TEHL2MPFireBullets) + SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD ), + SendPropVector( SENDINFO(m_vecDir), -1 ), + SendPropInt( SENDINFO( m_iAmmoID ), 5, SPROP_UNSIGNED ), + SendPropInt( SENDINFO( m_iSeed ), NUM_BULLET_SEED_BITS, SPROP_UNSIGNED ), + SendPropInt( SENDINFO( m_iShots ), 5, SPROP_UNSIGNED ), + SendPropInt( SENDINFO( m_iPlayer ), 6, SPROP_UNSIGNED ), // max 64 players, see MAX_PLAYERS + SendPropFloat( SENDINFO( m_flSpread ), 10, 0, 0, 1 ), + SendPropBool( SENDINFO( m_bDoImpacts ) ), + SendPropBool( SENDINFO( m_bDoTracers ) ), +END_SEND_TABLE() + + +// Singleton +static CTEHL2MPFireBullets g_TEHL2MPFireBullets( "Shotgun Shot" ); + + +void TE_HL2MPFireBullets( + int iPlayerIndex, + const Vector &vOrigin, + const Vector &vDir, + int iAmmoID, + int iSeed, + int iShots, + float flSpread, + bool bDoTracers, + bool bDoImpacts ) +{ + CPASFilter filter( vOrigin ); + filter.UsePredictionRules(); + + g_TEHL2MPFireBullets.m_iPlayer = iPlayerIndex; + g_TEHL2MPFireBullets.m_vecOrigin = vOrigin; + g_TEHL2MPFireBullets.m_vecDir = vDir; + g_TEHL2MPFireBullets.m_iSeed = iSeed; + g_TEHL2MPFireBullets.m_iShots = iShots; + g_TEHL2MPFireBullets.m_flSpread = flSpread; + g_TEHL2MPFireBullets.m_iAmmoID = iAmmoID; + g_TEHL2MPFireBullets.m_bDoTracers = bDoTracers; + g_TEHL2MPFireBullets.m_bDoImpacts = bDoImpacts; + + Assert( iSeed < (1 << NUM_BULLET_SEED_BITS) ); + + g_TEHL2MPFireBullets.Create( filter, 0 ); +} diff --git a/game/server/hl2mp/te_hl2mp_shotgun_shot.h b/game/server/hl2mp/te_hl2mp_shotgun_shot.h new file mode 100644 index 0000000..d6a6833 --- /dev/null +++ b/game/server/hl2mp/te_hl2mp_shotgun_shot.h @@ -0,0 +1,26 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//=============================================================================// + +#ifndef TE_HL2MP_SHOTGUN_SHOT_H +#define TE_HL2MP_SHOTGUN_SHOT_H +#ifdef _WIN32 +#pragma once +#endif + + +void TE_HL2MPFireBullets( + int iPlayerIndex, + const Vector &vOrigin, + const Vector &vDir, + int iAmmoID, + int iSeed, + int iShots, + float flSpread, + bool bDoTracers, + bool bDoImpacts ); + + +#endif // TE_HL2MP_SHOTGUN_SHOT_H |