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.
396 lines
9.6 KiB
396 lines
9.6 KiB
5 years ago
|
//========= Copyright Valve Corporation, All rights reserved. ============//
|
||
|
//
|
||
|
// Purpose:
|
||
|
//
|
||
|
//=============================================================================//
|
||
|
|
||
|
#include "cbase.h"
|
||
|
#include "tf_walker_strider.h"
|
||
|
#include "npcevent.h"
|
||
|
#include "engine/IEngineSound.h"
|
||
|
#include "tf_gamerules.h"
|
||
|
#include "shake.h"
|
||
|
#include "in_buttons.h"
|
||
|
#include "studio.h"
|
||
|
|
||
|
|
||
|
#define STRIDER_AE_FOOTSTEP_LEFT 1
|
||
|
#define STRIDER_AE_FOOTSTEP_RIGHT 2
|
||
|
#define STRIDER_AE_FOOTSTEP_BACK 3
|
||
|
#define STRIDER_AE_FOOTSTEP_LEFTM 4
|
||
|
#define STRIDER_AE_FOOTSTEP_RIGHTM 5
|
||
|
#define STRIDER_AE_FOOTSTEP_BACKM 6
|
||
|
#define STRIDER_AE_FOOTSTEP_LEFTL 7
|
||
|
#define STRIDER_AE_FOOTSTEP_RIGHTL 8
|
||
|
#define STRIDER_AE_FOOTSTEP_BACKL 9
|
||
|
|
||
|
|
||
|
// How many inches/sec the strider's torso moves up and down to get the torso at the right height.
|
||
|
#define STRIDER_TORSO_VERTICAL_SLIDE_SPEED 100
|
||
|
#define STRIDER_FIRE_TIME 5
|
||
|
#define STRIDER_FIRE_INTERVAL 0.3
|
||
|
#define STRIDER_FIRE_ANGLE_ERROR 6 // N degrees of error when he fires.
|
||
|
#define WALKER_STRIDER_MODEL "models/objects/walker_strider.mdl"
|
||
|
|
||
|
|
||
|
IMPLEMENT_SERVERCLASS_ST( CWalkerStrider, DT_WalkerStrider )
|
||
|
SendPropInt( SENDINFO( m_bCrouched ), 1, SPROP_UNSIGNED )
|
||
|
END_SEND_TABLE()
|
||
|
LINK_ENTITY_TO_CLASS( walker_strider, CWalkerStrider );
|
||
|
PRECACHE_REGISTER( walker_strider );
|
||
|
|
||
|
|
||
|
char *g_StriderFeet[] =
|
||
|
{
|
||
|
"left foot",
|
||
|
"right foot",
|
||
|
"back foot"
|
||
|
};
|
||
|
#define NUM_STRIDER_FEET ARRAYSIZE( g_StriderFeet )
|
||
|
|
||
|
|
||
|
CWalkerStrider::CWalkerStrider()
|
||
|
{
|
||
|
m_bCrouched = false;
|
||
|
m_flOriginToLowestLegHeight = -1;
|
||
|
m_flWantedZ = -1;
|
||
|
m_bFiring = false;
|
||
|
m_vDriverAngles.Init();
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::Precache()
|
||
|
{
|
||
|
PrecacheModel( WALKER_STRIDER_MODEL );
|
||
|
|
||
|
PrecacheScriptSound( "Brush.Footstep" );
|
||
|
PrecacheScriptSound( "Brush.Fire" );
|
||
|
|
||
|
BaseClass::Precache();
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::Spawn()
|
||
|
{
|
||
|
SpawnWalker(
|
||
|
WALKER_STRIDER_MODEL, // Model name.
|
||
|
OBJ_WALKER_STRIDER, // Object type.
|
||
|
Vector( -186, -157, -504 ), // Placement dimensions.
|
||
|
Vector( 194, 159, 41 ),
|
||
|
200, // Health.
|
||
|
1, // Max passengers.
|
||
|
2.0 // 2x animation speed boost
|
||
|
);
|
||
|
}
|
||
|
|
||
|
bool CWalkerStrider::IsPassengerVisible( int nRole )
|
||
|
{
|
||
|
if ( nRole == VEHICLE_ROLE_DRIVER )
|
||
|
return false;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::Fire()
|
||
|
{
|
||
|
EnableWalkMode( false );
|
||
|
m_bFiring = true;
|
||
|
m_flFireEndTime = gpGlobals->curtime + STRIDER_FIRE_TIME;
|
||
|
ResetSequence( LookupSequence( "shoot01a" ) );
|
||
|
m_flAnimTime = m_flPrevAnimTime = 0;
|
||
|
SetCycle( 0 );
|
||
|
m_flNextShootTime = gpGlobals->curtime;
|
||
|
|
||
|
m_vFireAngles[ROLL] = 0;
|
||
|
m_vFireAngles[PITCH] = m_vDriverAngles[PITCH];
|
||
|
m_vFireAngles[YAW] = GetAbsAngles()[YAW];
|
||
|
|
||
|
// Play the fire sound.
|
||
|
CPASAttenuationFilter filter( this, "Brush.Fire" );
|
||
|
EmitSound( filter, 0, "Brush.Fire", &GetAbsOrigin() );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::Crouch()
|
||
|
{
|
||
|
// Disable the base class's walking functionality while we're crouched.
|
||
|
EnableWalkMode( false );
|
||
|
|
||
|
m_bCrouched = true;
|
||
|
m_flNextCrouchTime = gpGlobals->curtime + 0.3;
|
||
|
ResetSequence( LookupSequence( "low" ) );
|
||
|
|
||
|
// HACK: there should be a better way to this.. like CBaseAnimating::ResetAnimation,
|
||
|
// or ResetSequence should do it.
|
||
|
m_flAnimTime = m_flPrevAnimTime = 0;
|
||
|
SetCycle( 0 );
|
||
|
|
||
|
// HACK CITY.. This forces it to invalidate the abs origins of the gun bases.
|
||
|
Vector vPos = GetLocalOrigin();
|
||
|
SetLocalOrigin( vPos + Vector( 100, 0, 0 ) );
|
||
|
SetLocalOrigin( vPos );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::UnCrouch()
|
||
|
{
|
||
|
EnableWalkMode( true );
|
||
|
m_flNextCrouchTime = gpGlobals->curtime + 0.3;
|
||
|
m_bCrouched = false;
|
||
|
|
||
|
// HACK CITY.. This forces it to invalidate the abs origins of the gun bases.
|
||
|
Vector vPos = GetLocalOrigin();
|
||
|
SetLocalOrigin( vPos + Vector( 100, 0, 0 ) );
|
||
|
SetLocalOrigin( vPos );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::WalkerThink()
|
||
|
{
|
||
|
float dt = GetTimeDelta();
|
||
|
|
||
|
BaseClass::WalkerThink();
|
||
|
|
||
|
if ( m_bCrouched )
|
||
|
{
|
||
|
// Just sit there until they hit IN_DUCK again.
|
||
|
if ( (m_LastButtons & IN_DUCK) && gpGlobals->curtime > m_flNextCrouchTime )
|
||
|
{
|
||
|
UnCrouch();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ( gpGlobals->curtime > m_flNextCrouchTime )
|
||
|
{
|
||
|
if ( m_LastButtons & IN_DUCK )
|
||
|
{
|
||
|
// They want to crouch.
|
||
|
Crouch();
|
||
|
}
|
||
|
else if ( !m_bFiring && (m_LastButtons & IN_ATTACK) )
|
||
|
{
|
||
|
Fire();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
m_vDriverAngles = m_vLastCmdViewAngles;
|
||
|
if ( m_bCrouched )
|
||
|
{
|
||
|
// The "low" animation gets back up at the end so don't let that happen.
|
||
|
SetCycle( MIN( GetCycle(), 0.5f ) );
|
||
|
}
|
||
|
else if ( m_bFiring )
|
||
|
{
|
||
|
if ( gpGlobals->curtime > m_flFireEndTime )
|
||
|
{
|
||
|
m_bFiring = false;
|
||
|
EnableWalkMode( true );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Shoot?
|
||
|
if ( gpGlobals->curtime >= m_flNextShootTime )
|
||
|
{
|
||
|
Vector vSrc = GetAbsOrigin();
|
||
|
Vector vForward;
|
||
|
|
||
|
QAngle vFireAngles = m_vFireAngles;
|
||
|
vFireAngles[YAW] += RandomFloat( -STRIDER_FIRE_ANGLE_ERROR, STRIDER_FIRE_ANGLE_ERROR );
|
||
|
vFireAngles[PITCH] += RandomFloat( -STRIDER_FIRE_ANGLE_ERROR, STRIDER_FIRE_ANGLE_ERROR );
|
||
|
|
||
|
AngleVectors( vFireAngles, &vForward );
|
||
|
trace_t trace;
|
||
|
UTIL_TraceLine( vSrc, vSrc + vForward * 2000, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace );
|
||
|
|
||
|
Vector vHitPos = trace.endpos;
|
||
|
float flDamageRadius = 100;
|
||
|
float flDamage = 50;
|
||
|
|
||
|
CBasePlayer *pDriver = GetPassenger( VEHICLE_ROLE_DRIVER );
|
||
|
if ( pDriver )
|
||
|
{
|
||
|
UTIL_ImpactTrace( &trace, DMG_ENERGYBEAM, "Strider" );
|
||
|
|
||
|
// Tell the client entity to make a beam effect.
|
||
|
EntityMessageBegin( this, true );
|
||
|
WRITE_VEC3COORD( vHitPos );
|
||
|
MessageEnd();
|
||
|
|
||
|
UTIL_ScreenShake( vHitPos, 10.0, 150.0, 1.0, 100, SHAKE_START );
|
||
|
RadiusDamage( CTakeDamageInfo( this, pDriver, flDamage, DMG_BLAST ), vHitPos, flDamageRadius, CLASS_NONE, NULL );
|
||
|
}
|
||
|
|
||
|
m_flNextShootTime = gpGlobals->curtime + STRIDER_FIRE_INTERVAL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Move our torso within range of our feet.
|
||
|
if ( m_flOriginToLowestLegHeight != -1 )
|
||
|
{
|
||
|
trace_t trace;
|
||
|
UTIL_TraceLine(
|
||
|
GetAbsOrigin(),
|
||
|
GetAbsOrigin() - Vector( 0, 0, 2000 ),
|
||
|
MASK_SOLID_BRUSHONLY,
|
||
|
this,
|
||
|
COLLISION_GROUP_NONE,
|
||
|
&trace );
|
||
|
|
||
|
if ( trace.fraction < 1 )
|
||
|
{
|
||
|
m_flWantedZ = trace.endpos.z + m_flOriginToLowestLegHeight;
|
||
|
}
|
||
|
|
||
|
// Move our Z towards the wanted Z.
|
||
|
if ( m_flWantedZ != -1 )
|
||
|
{
|
||
|
Vector vCur = GetAbsOrigin();
|
||
|
vCur.z = Approach( m_flWantedZ, vCur.z, STRIDER_TORSO_VERTICAL_SLIDE_SPEED * dt );
|
||
|
SetAbsOrigin( vCur );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
|
||
|
{
|
||
|
// Sapper removal
|
||
|
if ( RemoveEnemyAttachments( pActivator ) )
|
||
|
return;
|
||
|
|
||
|
CBaseTFPlayer *pPlayer = dynamic_cast<CBaseTFPlayer*>(pActivator);
|
||
|
if ( !pPlayer || !InSameTeam( pPlayer ) )
|
||
|
return;
|
||
|
|
||
|
// Ok, put them in the driver role.
|
||
|
AttemptToBoardVehicle( pPlayer );
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::SetupMove( CBasePlayer *pPlayer, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
|
||
|
{
|
||
|
BaseClass::SetupMove( pPlayer, ucmd, pHelper, move );
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
//
|
||
|
// This is a TOTAL hack, but we don't have any nodes that work well at all for mounted guns.
|
||
|
// This all goes away when we get a new model.
|
||
|
//
|
||
|
//
|
||
|
float sideDist = 90;
|
||
|
float downDist = -400;
|
||
|
bool CWalkerStrider::GetAttachment( int iAttachment, matrix3x4_t &attachmentToWorld )
|
||
|
{
|
||
|
CStudioHdr *pStudioHdr = GetModelPtr( );
|
||
|
if ( !pStudioHdr || iAttachment < 1 || iAttachment > pStudioHdr->GetNumAttachments() )
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
Vector vLocalPos( 0, 0, 0 );
|
||
|
const mstudioattachment_t &pAttachment = pStudioHdr->pAttachment( iAttachment-1 );
|
||
|
if ( stricmp( pAttachment.pszName(), "build_point_left_gun" ) == 0 )
|
||
|
{
|
||
|
vLocalPos.y = sideDist;
|
||
|
}
|
||
|
else if ( stricmp( pAttachment.pszName(), "build_point_right_gun" ) == 0 )
|
||
|
{
|
||
|
vLocalPos.y = -sideDist;
|
||
|
}
|
||
|
else if ( stricmp( pAttachment.pszName(), "ThirdPersonCameraOrigin" ) == 0 )
|
||
|
{
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Ok, it's not one of our magical attachments. Use the regular attachment setup stuff.
|
||
|
return BaseClass::GetAttachment( iAttachment, attachmentToWorld );
|
||
|
}
|
||
|
|
||
|
if ( m_bCrouched )
|
||
|
{
|
||
|
vLocalPos.z += downDist;
|
||
|
}
|
||
|
|
||
|
// Now build the output matrix.
|
||
|
matrix3x4_t localMatrix;
|
||
|
SetIdentityMatrix( localMatrix );
|
||
|
PositionMatrix( vLocalPos, localMatrix );
|
||
|
|
||
|
ConcatTransforms( EntityToWorldTransform(), localMatrix, attachmentToWorld );
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
bool CWalkerStrider::StartBuilding( CBaseEntity *pBuilder )
|
||
|
{
|
||
|
if ( !BaseClass::StartBuilding( pBuilder ) )
|
||
|
return false;
|
||
|
|
||
|
// Now figure out our ideal Z distance from our lowest foot to our torso.
|
||
|
Vector vOrigin;
|
||
|
QAngle vAngles;
|
||
|
BaseClass::GetAttachment( "left foot", vOrigin, vAngles );
|
||
|
m_flOriginToLowestLegHeight = GetAbsOrigin().z - vOrigin.z;
|
||
|
m_flOriginToLowestLegHeight -= 40; // fudge it a little so the legs have room to stretch.
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::FootHit( const char *pFootName )
|
||
|
{
|
||
|
if ( m_bCrouched || gpGlobals->curtime > m_flDontMakeSoundsUntil )
|
||
|
{
|
||
|
Vector footPosition;
|
||
|
QAngle angles;
|
||
|
|
||
|
((BaseClass*)this)->GetAttachment( pFootName, footPosition, angles );
|
||
|
CPASAttenuationFilter filter( this, "Brush.Footstep" );
|
||
|
EmitSound( filter, entindex(), "Brush.Footstep", &footPosition );
|
||
|
|
||
|
UTIL_ScreenShake( footPosition, 20.0, 1.0, 0.5, 200, SHAKE_START, false );
|
||
|
|
||
|
CBasePlayer *pPlayer = GetPassenger( VEHICLE_ROLE_DRIVER );
|
||
|
if ( pPlayer )
|
||
|
{
|
||
|
CTakeDamageInfo info( this, pPlayer, 20, DMG_CLUB );
|
||
|
TFGameRules()->RadiusDamage( info, footPosition, 200, CLASS_NONE );
|
||
|
}
|
||
|
|
||
|
m_flDontMakeSoundsUntil = gpGlobals->curtime + 0.4;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void CWalkerStrider::HandleAnimEvent( animevent_t *pEvent )
|
||
|
{
|
||
|
switch( pEvent->event )
|
||
|
{
|
||
|
case STRIDER_AE_FOOTSTEP_LEFT:
|
||
|
case STRIDER_AE_FOOTSTEP_LEFTM:
|
||
|
case STRIDER_AE_FOOTSTEP_LEFTL:
|
||
|
FootHit( "left foot" );
|
||
|
break;
|
||
|
|
||
|
case STRIDER_AE_FOOTSTEP_RIGHT:
|
||
|
case STRIDER_AE_FOOTSTEP_RIGHTM:
|
||
|
case STRIDER_AE_FOOTSTEP_RIGHTL:
|
||
|
FootHit( "right foot" );
|
||
|
break;
|
||
|
|
||
|
case STRIDER_AE_FOOTSTEP_BACK:
|
||
|
case STRIDER_AE_FOOTSTEP_BACKM:
|
||
|
case STRIDER_AE_FOOTSTEP_BACKL:
|
||
|
FootHit( "back foot" );
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|