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