source-engine/game/server/hl1/hl1mp_player.cpp
2022-04-16 12:05:19 +03:00

637 lines
16 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Multiplayer Player for HL1.
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "hl1mp_player.h"
#include "client.h"
#include "team.h"
class CTEPlayerAnimEvent : public CBaseTempEntity
{
public:
DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
DECLARE_SERVERCLASS();
CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
{
}
CNetworkHandle( CBasePlayer, m_hPlayer );
CNetworkVar( int, m_iEvent );
CNetworkVar( int, m_nData );
};
IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
SendPropEHandle( SENDINFO( m_hPlayer ) ),
SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
SendPropInt( SENDINFO( m_nData ), 32 )
END_SEND_TABLE()
static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
{
CPVSFilter filter( pPlayer->EyePosition() );
// The player himself doesn't need to be sent his animation events
// unless cs_showanimstate wants to show them.
// if ( !ToolsEnabled() && ( cl_showanimstate.GetInt() == pPlayer->entindex() ) )
{
// filter.RemoveRecipient( pPlayer );
}
g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
g_TEPlayerAnimEvent.m_iEvent = event;
g_TEPlayerAnimEvent.m_nData = nData;
g_TEPlayerAnimEvent.Create( filter, 0 );
}
//////////////////////////////////////////////////////////////////////////////////////////
extern int gEvilImpulse101;
LINK_ENTITY_TO_CLASS( player_mp, CHL1MP_Player );
PRECACHE_REGISTER( player_mp );
IMPLEMENT_SERVERCLASS_ST( CHL1MP_Player, DT_HL1MP_PLAYER )
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" ),
// cs_playeranimstate and clientside animation takes care of these on the client
// SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 11 ),
SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 11 ),
SendPropEHandle( SENDINFO( m_hRagdoll ) ),
SendPropInt( SENDINFO( m_iSpawnInterpCounter), 4 ),
SendPropInt( SENDINFO( m_iRealSequence ), 9 ),
// SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_TFCPlayerShared ) )
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 );
static const char * s_szModelPath = "models/player/mp/";
CHL1MP_Player::CHL1MP_Player()
{
m_PlayerAnimState = CreatePlayerAnimState( this );
// item_list = 0;
UseClientSideAnimation();
m_angEyeAngles.Init();
// m_pCurStateInfo = NULL;
m_lifeState = LIFE_DEAD; // Start "dead".
m_iSpawnInterpCounter = 0;
m_flNextModelChangeTime = 0;
m_flNextTeamChangeTime = 0;
// SetViewOffset( TFC_PLAYER_VIEW_OFFSET );
// SetContextThink( &CTFCPlayer::TFCPlayerThink, gpGlobals->curtime, "TFCPlayerThink" );
}
CHL1MP_Player::~CHL1MP_Player()
{
m_PlayerAnimState->Release();
}
void CHL1MP_Player::PostThink( void )
{
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 CHL1MP_Player::Spawn( void )
{
if ( !IsObserver() )
{
RemoveEffects( EF_NODRAW );
SetMoveType( MOVETYPE_WALK );
RemoveSolidFlags( FSOLID_NOT_SOLID );
// if no model, force one
if ( !GetModelPtr() )
SetModel( "models/player/mp/gordon/gordon.mdl" );
}
m_flNextModelChangeTime = 0;
m_flNextTeamChangeTime = 0;
BaseClass::Spawn();
if ( !IsObserver() )
{
GiveDefaultItems();
SetPlayerModel();
}
m_bHasLongJump = false;
m_iSpawnInterpCounter = (m_iSpawnInterpCounter + 1) % 8;
}
void CHL1MP_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
{
m_PlayerAnimState->DoAnimationEvent( event, nData );
TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
}
void CHL1MP_Player::GiveDefaultItems( void )
{
GiveNamedItem( "weapon_crowbar" );
GiveNamedItem( "weapon_glock" );
CBasePlayer::GiveAmmo( 68, "9mmRound" );
}
void CHL1MP_Player::UpdateOnRemove( void )
{
if ( m_hRagdoll )
{
UTIL_RemoveImmediate( m_hRagdoll );
m_hRagdoll = NULL;
}
BaseClass::UpdateOnRemove();
}
void CHL1MP_Player::DetonateSatchelCharges( void )
{
CBaseEntity *pSatchel = NULL;
while ( (pSatchel = gEntList.FindEntityByClassname( pSatchel, "monster_satchel" ) ) != NULL)
{
if ( pSatchel->GetOwnerEntity() == this )
{
pSatchel->Use( this, this, USE_ON, 0 );
}
}
}
void CHL1MP_Player::Event_Killed( const CTakeDamageInfo &info )
{
DoAnimationEvent( PLAYERANIMEVENT_DIE );
// 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.
if ( !IsHLTV() )
CreateRagdollEntity();
DetonateSatchelCharges();
BaseClass::Event_Killed( info );
m_lifeState = LIFE_DEAD;
RemoveEffects( EF_NODRAW ); // still draw player body
}
void CHL1MP_Player::SetAnimation( PLAYER_ANIM playerAnim )
{
// BaseClass::SetAnimation( playerAnim );
if ( playerAnim == PLAYER_ATTACK1 )
{
DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN );
}
int animDesired;
char szAnim[64];
float speed;
speed = GetAbsVelocity().Length2D();
if (GetFlags() & (FL_FROZEN|FL_ATCONTROLS))
{
speed = 0;
playerAnim = PLAYER_IDLE;
}
if ( playerAnim == PLAYER_ATTACK1 )
{
if ( speed > 0 )
{
playerAnim = PLAYER_WALK;
}
else
{
playerAnim = PLAYER_IDLE;
}
}
Activity idealActivity = ACT_WALK;// TEMP!!!!!
// This could stand to be redone. Why is playerAnim abstracted from activity? (sjb)
if (playerAnim == PLAYER_JUMP)
{
idealActivity = ACT_HOP;
}
else if (playerAnim == PLAYER_SUPERJUMP)
{
idealActivity = ACT_LEAP;
}
else if (playerAnim == PLAYER_DIE)
{
if ( m_lifeState == LIFE_ALIVE )
{
idealActivity = ACT_DIERAGDOLL;
}
}
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_RANGE_ATTACK1;
}
}
else if (playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK)
{
if ( !( GetFlags() & FL_ONGROUND ) && (GetActivity() == ACT_HOP || GetActivity() == ACT_LEAP) ) // Still jumping
{
idealActivity = GetActivity();
}
else if ( GetWaterLevel() > 1 )
{
if ( speed == 0 )
idealActivity = ACT_HOVER;
else
idealActivity = ACT_SWIM;
}
else if ( speed > 0 )
{
idealActivity = ACT_WALK;
}
else
{
idealActivity = ACT_IDLE;
}
}
if (idealActivity == ACT_RANGE_ATTACK1)
{
if ( GetFlags() & FL_DUCKING ) // crouching
{
Q_strncpy( szAnim, "crouch_shoot_" ,sizeof(szAnim));
}
else
{
Q_strncpy( szAnim, "ref_shoot_" ,sizeof(szAnim));
}
Q_strncat( szAnim, m_szAnimExtension ,sizeof(szAnim), COPY_ALL_CHARACTERS );
animDesired = LookupSequence( szAnim );
if (animDesired == -1)
animDesired = 0;
if ( GetSequence() != animDesired || !SequenceLoops() )
{
SetCycle( 0 );
}
// Tracker 24588: In single player when firing own weapon this causes eye and punchangle to jitter
//if (!SequenceLoops())
//{
// IncrementInterpolationFrame();
//}
SetActivity( idealActivity );
ResetSequence( animDesired );
}
else if (idealActivity == ACT_IDLE)
{
if ( GetFlags() & FL_DUCKING )
{
animDesired = LookupSequence( "crouch_idle" );
}
else
{
animDesired = LookupSequence( "look_idle" );
}
if (animDesired == -1)
animDesired = 0;
SetActivity( ACT_IDLE );
}
else if ( idealActivity == ACT_WALK )
{
if ( GetFlags() & FL_DUCKING )
{
animDesired = SelectWeightedSequence( ACT_CROUCH );
SetActivity( ACT_CROUCH );
}
else
{
animDesired = SelectWeightedSequence( ACT_RUN );
SetActivity( ACT_RUN );
}
}
else
{
if ( GetActivity() == idealActivity)
return;
SetActivity( idealActivity );
animDesired = SelectWeightedSequence( GetActivity() );
// Already using the desired animation?
if (GetSequence() == animDesired)
return;
m_iRealSequence = animDesired;
ResetSequence( animDesired );
SetCycle( 0 );
return;
}
// Already using the desired animation?
if (GetSequence() == animDesired)
return;
m_iRealSequence = animDesired;
//Msg( "Set animation to %d\n", animDesired );
// Reset to first frame of desired animation
ResetSequence( animDesired );
SetCycle( 0 );
}
static ConVar sv_debugweaponpickup( "sv_debugweaponpickup", "0", FCVAR_CHEAT, "Prints descriptive reasons as to why pickup did not work." );
// correct respawning of weapons
bool CHL1MP_Player::BumpWeapon( CBaseCombatWeapon *pWeapon )
{ CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
// Can I have this weapon type?
if ( !IsAllowedToPickupWeapons() )
{
if ( sv_debugweaponpickup.GetBool() )
Msg("sv_debugweaponpickup: IsAllowedToPickupWeapons() returned false\n");
return false;
}
if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
{
if ( sv_debugweaponpickup.GetBool() && pOwner )
Msg("sv_debugweaponpickup: pOwner\n");
if ( sv_debugweaponpickup.GetBool() && !Weapon_CanUse( pWeapon ) )
Msg("sv_debugweaponpickup: Can't use weapon\n");
if ( sv_debugweaponpickup.GetBool() && !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
Msg("sv_debugweaponpickup: Gamerules says player can't have item\n");
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) )
{
if ( sv_debugweaponpickup.GetBool() && !FVisible( this, MASK_SOLID ) )
Msg("sv_debugweaponpickup: Can't fetch weapon through a wall\n");
if ( sv_debugweaponpickup.GetBool() && !(GetFlags() & FL_NOTARGET) )
Msg("sv_debugweaponpickup: NoTarget\n");
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 );
if ( sv_debugweaponpickup.GetBool() )
Msg("sv_debugweaponpickup: Picking up weapon\n");
return true;
}
else
{
if ( sv_debugweaponpickup.GetBool() )
Msg("sv_debugweaponpickup: Owns weapon already\n");
return false;
}
}
pWeapon->CheckRespawn();
Weapon_Equip( pWeapon );
if ( sv_debugweaponpickup.GetBool() )
Msg("sv_debugweaponpickup: Picking up weapon\n");
return true;
}
void CHL1MP_Player::ChangeTeam( int iTeamNum )
{
bool bKill = false;
if ( g_pGameRules->IsTeamplay() == true )
{
if ( iTeamNum != GetTeamNumber() && GetTeamNumber() != TEAM_UNASSIGNED )
{
bKill = true;
}
}
BaseClass::ChangeTeam( iTeamNum );
m_flNextTeamChangeTime = gpGlobals->curtime + 5;
if ( g_pGameRules->IsTeamplay() == true )
{
SetPlayerTeamModel();
}
else
{
SetPlayerModel();
}
if ( bKill == true )
{
CommitSuicide();
}
}
void CHL1MP_Player::SetPlayerTeamModel( void )
{
int iTeamNum = GetTeamNumber();
if ( iTeamNum <= TEAM_SPECTATOR )
return;
CTeam * pTeam = GetGlobalTeam( iTeamNum );
char szModelName[256];
Q_snprintf( szModelName, 256, "%s%s/%s.mdl", s_szModelPath, pTeam->GetName(), pTeam->GetName() );
// Check to see if the model was properly precached, do not error out if not.
int i = modelinfo->GetModelIndex( szModelName );
if ( i == -1 )
{
Warning("Model %s does not exist.\n", szModelName );
return;
}
SetModel( szModelName );
m_flNextModelChangeTime = gpGlobals->curtime + 5;
}
void CHL1MP_Player::SetPlayerModel( void )
{
char szBaseName[128];
Q_FileBase( engine->GetClientConVarValue( engine->IndexOfEdict( edict() ), "cl_playermodel" ), szBaseName, 128 );
// Don't let it be 'none'; default to Barney
if ( Q_stricmp( "none", szBaseName ) == 0 )
{
Q_strcpy( szBaseName, "gordon" );
}
char szModelName[256];
Q_snprintf( szModelName, 256, "%s%s/%s.mdl", s_szModelPath, szBaseName, szBaseName );
// Check to see if the model was properly precached, do not error out if not.
int i = modelinfo->GetModelIndex( szModelName );
if ( i == -1 )
{
SetModel( "models/player/mp/gordon/gordon.mdl" );
engine->ClientCommand ( edict(), "cl_playermodel models/gordon.mdl\n" );
return;
}
SetModel( szModelName );
m_flNextModelChangeTime = gpGlobals->curtime + 5;
}
// -------------------------------------------------------------------------------- //
// Ragdoll entities.
// -------------------------------------------------------------------------------- //
class CHL1MPRagdoll : public CBaseAnimatingOverlay
{
public:
DECLARE_CLASS( CHL1MPRagdoll, 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( hl1mp_ragdoll, CHL1MPRagdoll );
IMPLEMENT_SERVERCLASS_ST_NOBASE( CHL1MPRagdoll, DT_HL1MPRagdoll )
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 CHL1MP_Player::CreateRagdollEntity( void )
{
if ( m_hRagdoll )
{
UTIL_RemoveImmediate( m_hRagdoll );
m_hRagdoll = NULL;
}
// If we already have a ragdoll, don't make another one.
CHL1MPRagdoll *pRagdoll = dynamic_cast< CHL1MPRagdoll* >(m_hRagdoll.Get() );
if ( !pRagdoll )
{
// Create a new one
pRagdoll = dynamic_cast< CHL1MPRagdoll* >( CreateEntityByName( "hl1mp_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() );
}
m_hRagdoll = pRagdoll;
}
void CHL1MP_Player::CreateCorpse( void )
{
}