|
|
//========= 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 ); }
|