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.
366 lines
9.9 KiB
366 lines
9.9 KiB
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Player for HL1.
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "sdk_player.h"
|
|
#include "sdk_gamerules.h"
|
|
#include "weapon_sdkbase.h"
|
|
#include "predicted_viewmodel.h"
|
|
#include "iservervehicle.h"
|
|
#include "viewport_panel_names.h"
|
|
|
|
extern int gEvilImpulse101;
|
|
|
|
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." );
|
|
|
|
#define SDK_PLAYER_MODEL "models/player/terror.mdl"
|
|
|
|
|
|
// -------------------------------------------------------------------------------- //
|
|
// 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 );
|
|
};
|
|
|
|
#define THROWGRENADE_COUNTER_BITS 3
|
|
|
|
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 );
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------- //
|
|
// Tables.
|
|
// -------------------------------------------------------------------------------- //
|
|
BEGIN_DATADESC( CSDKPlayer )
|
|
DEFINE_THINKFUNC( SDKPushawayThink ),
|
|
END_DATADESC()
|
|
|
|
LINK_ENTITY_TO_CLASS( player, CSDKPlayer );
|
|
PRECACHE_REGISTER(player);
|
|
|
|
BEGIN_SEND_TABLE_NOBASE( CSDKPlayer, DT_SDKLocalPlayerExclusive )
|
|
SendPropInt( SENDINFO( m_iShotsFired ), 8, SPROP_UNSIGNED ),
|
|
END_SEND_TABLE()
|
|
|
|
IMPLEMENT_SERVERCLASS_ST( CSDKPlayer, DT_SDKPlayer )
|
|
SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
|
|
SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
|
|
SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
|
|
SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
|
|
SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
|
|
|
|
// playeranimstate and clientside animation takes care of these on the client
|
|
SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
|
|
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
|
|
|
|
// Data that only gets sent to the local player.
|
|
SendPropDataTable( "sdklocaldata", 0, &REFERENCE_SEND_TABLE(DT_SDKLocalPlayerExclusive), SendProxy_SendLocalDataTable ),
|
|
|
|
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11 ),
|
|
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11 ),
|
|
SendPropEHandle( SENDINFO( m_hRagdoll ) ),
|
|
|
|
SendPropInt( SENDINFO( m_iThrowGrenadeCounter ), THROWGRENADE_COUNTER_BITS, SPROP_UNSIGNED ),
|
|
END_SEND_TABLE()
|
|
|
|
class CSDKRagdoll : public CBaseAnimatingOverlay
|
|
{
|
|
public:
|
|
DECLARE_CLASS( CSDKRagdoll, 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( sdk_ragdoll, CSDKRagdoll );
|
|
|
|
IMPLEMENT_SERVERCLASS_ST_NOBASE( CSDKRagdoll, DT_SDKRagdoll )
|
|
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 cc_CreatePredictionError_f()
|
|
{
|
|
CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
|
|
pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) );
|
|
}
|
|
|
|
ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
|
|
|
|
|
|
CSDKPlayer::CSDKPlayer()
|
|
{
|
|
m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true );
|
|
|
|
UseClientSideAnimation();
|
|
m_angEyeAngles.Init();
|
|
|
|
SetViewOffset( SDK_PLAYER_VIEW_OFFSET );
|
|
|
|
m_iThrowGrenadeCounter = 0;
|
|
}
|
|
|
|
|
|
CSDKPlayer::~CSDKPlayer()
|
|
{
|
|
m_PlayerAnimState->Release();
|
|
}
|
|
|
|
|
|
CSDKPlayer *CSDKPlayer::CreatePlayer( const char *className, edict_t *ed )
|
|
{
|
|
CSDKPlayer::s_PlayerEdict = ed;
|
|
return (CSDKPlayer*)CreateEntityByName( className );
|
|
}
|
|
|
|
void CSDKPlayer::LeaveVehicle( const Vector &vecExitPoint, const QAngle &vecExitAngles )
|
|
{
|
|
BaseClass::LeaveVehicle( vecExitPoint, vecExitAngles );
|
|
|
|
//teleport physics shadow too
|
|
// Vector newPos = GetAbsOrigin();
|
|
// QAngle newAng = GetAbsAngles();
|
|
|
|
// Teleport( &newPos, &newAng, &vec3_origin );
|
|
}
|
|
|
|
void CSDKPlayer::PreThink(void)
|
|
{
|
|
// Riding a vehicle?
|
|
if ( IsInAVehicle() )
|
|
{
|
|
// make sure we update the client, check for timed damage and update suit even if we are in a vehicle
|
|
UpdateClientData();
|
|
CheckTimeBasedDamage();
|
|
|
|
// Allow the suit to recharge when in the vehicle.
|
|
CheckSuitUpdate();
|
|
|
|
WaterMove();
|
|
return;
|
|
}
|
|
|
|
BaseClass::PreThink();
|
|
}
|
|
|
|
|
|
void CSDKPlayer::PostThink()
|
|
{
|
|
BaseClass::PostThink();
|
|
|
|
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] );
|
|
}
|
|
|
|
|
|
void CSDKPlayer::Precache()
|
|
{
|
|
PrecacheModel( SDK_PLAYER_MODEL );
|
|
|
|
BaseClass::Precache();
|
|
}
|
|
|
|
void CSDKPlayer::Spawn()
|
|
{
|
|
SetModel( SDK_PLAYER_MODEL );
|
|
SetMoveType( MOVETYPE_WALK );
|
|
RemoveSolidFlags( FSOLID_NOT_SOLID );
|
|
|
|
m_hRagdoll = NULL;
|
|
|
|
BaseClass::Spawn();
|
|
}
|
|
|
|
void CSDKPlayer::InitialSpawn( void )
|
|
{
|
|
BaseClass::InitialSpawn();
|
|
|
|
const ConVar *hostname = cvar->FindVar( "hostname" );
|
|
const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY";
|
|
|
|
// open info panel on client showing MOTD:
|
|
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_IMPULSE101 );// exec this command if panel closed
|
|
data->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() );
|
|
|
|
ShowViewPortPanel( PANEL_INFO, true, data );
|
|
|
|
data->deleteThis();
|
|
}
|
|
|
|
void CSDKPlayer::Event_Killed( const CTakeDamageInfo &info )
|
|
{
|
|
// 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.
|
|
|
|
BaseClass::Event_Killed( info );
|
|
|
|
CreateRagdollEntity();
|
|
}
|
|
|
|
void CSDKPlayer::CreateRagdollEntity()
|
|
{
|
|
// If we already have a ragdoll, don't make another one.
|
|
CSDKRagdoll *pRagdoll = dynamic_cast< CSDKRagdoll* >( m_hRagdoll.Get() );
|
|
|
|
if ( !pRagdoll )
|
|
{
|
|
// create a new one
|
|
pRagdoll = dynamic_cast< CSDKRagdoll* >( CreateEntityByName( "sdk_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 = Vector(0,0,0);
|
|
}
|
|
|
|
// ragdolls will be removed on round restart automatically
|
|
m_hRagdoll = pRagdoll;
|
|
}
|
|
|
|
void CSDKPlayer::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.
|
|
}
|
|
}
|
|
|
|
CWeaponSDKBase* CSDKPlayer::GetActiveSDKWeapon() const
|
|
{
|
|
return dynamic_cast< CWeaponSDKBase* >( GetActiveWeapon() );
|
|
}
|
|
|
|
void CSDKPlayer::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 );
|
|
}
|
|
}
|
|
|
|
void CSDKPlayer::CheatImpulseCommands( int iImpulse )
|
|
{
|
|
if ( iImpulse != 101 )
|
|
{
|
|
BaseClass::CheatImpulseCommands( iImpulse );
|
|
return ;
|
|
}
|
|
gEvilImpulse101 = true;
|
|
|
|
EquipSuit();
|
|
|
|
GiveNamedItem( "weapon_mp5" );
|
|
GiveNamedItem( "weapon_grenade" );
|
|
GiveNamedItem( "weapon_shotgun" );
|
|
|
|
// Give the player everything!
|
|
GiveAmmo( 90, AMMO_BULLETS );
|
|
GiveAmmo( 3, AMMO_GRENADE );
|
|
|
|
if ( GetHealth() < 100 )
|
|
{
|
|
TakeHealth( 25, DMG_GENERIC );
|
|
}
|
|
|
|
gEvilImpulse101 = false;
|
|
}
|
|
|
|
|
|
void CSDKPlayer::FlashlightTurnOn( void )
|
|
{
|
|
AddEffects( EF_DIMLIGHT );
|
|
}
|
|
|
|
void CSDKPlayer::FlashlightTurnOff( void )
|
|
{
|
|
RemoveEffects( EF_DIMLIGHT );
|
|
}
|
|
|
|
int CSDKPlayer::FlashlightIsOn( void )
|
|
{
|
|
return IsEffectActive( EF_DIMLIGHT );
|
|
}
|