Team Fortress 2 Source Code as on 22/4/2020
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.
 
 
 
 
 
 

518 lines
10 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: The Halflife Cycler NPCs
//
//=============================================================================//
#include "cbase.h"
#include "ai_basenpc.h"
#include "ai_motor.h"
#include "basecombatweapon.h"
#include "animation.h"
#include "vstdlib/random.h"
#include "h_cycler.h"
#include "Sprite.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define FCYCLER_NOTSOLID 0x0001
extern short g_sModelIndexSmoke; // (in combatweapon.cpp) holds the index for the smoke cloud
BEGIN_DATADESC( CCycler )
// Fields
DEFINE_FIELD( m_animate, FIELD_INTEGER ),
// Inputs
DEFINE_INPUTFUNC( FIELD_STRING, "SetSequence", InputSetSequence ),
END_DATADESC()
//
// we should get rid of all the other cyclers and replace them with this.
//
class CGenericCycler : public CCycler
{
public:
DECLARE_CLASS( CGenericCycler, CCycler );
void Spawn()
{
GenericCyclerSpawn( (char *)STRING( GetModelName() ), Vector(-16, -16, 0), Vector(16, 16, 72) );
}
};
LINK_ENTITY_TO_CLASS( cycler, CGenericCycler );
LINK_ENTITY_TO_CLASS( model_studio, CGenericCycler ); // For now model_studios build as cyclers.
// Cycler member functions
void CCycler::GenericCyclerSpawn(char *szModel, Vector vecMin, Vector vecMax)
{
if (!szModel || !*szModel)
{
Warning( "cycler at %.0f %.0f %0.f missing modelname\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z );
UTIL_Remove( this );
return;
}
Precache();
SetModel( szModel );
m_bloodColor = DONT_BLEED;
CCycler::Spawn( );
UTIL_SetSize(this, vecMin, vecMax);
}
void CCycler::Precache()
{
PrecacheModel( (const char *)STRING( GetModelName() ) );
}
void CCycler::Spawn( )
{
InitBoneControllers();
SetSolid( SOLID_BBOX );
if ( m_spawnflags & FCYCLER_NOTSOLID )
{
AddSolidFlags( FSOLID_NOT_SOLID );
}
else
{
AddSolidFlags( FSOLID_NOT_STANDABLE );
}
SetMoveType( MOVETYPE_NONE );
m_takedamage = DAMAGE_YES;
m_iHealth = 80000;// no cycler should die
GetMotor()->SetIdealYaw( GetLocalAngles().y );
GetMotor()->SnapYaw();
m_flPlaybackRate = 1.0;
m_flGroundSpeed = 0;
SetNextThink( gpGlobals->curtime + 1.0f );
ResetSequenceInfo( );
if (GetSequence() != 0 || m_flCycle != 0)
{
#ifdef TF2_DLL
m_animate = 1;
#else
m_animate = 0;
m_flPlaybackRate = 0;
#endif
}
else
{
m_animate = 1;
}
}
//
// cycler think
//
void CCycler::Think( void )
{
SetNextThink( gpGlobals->curtime + 0.1f );
if (m_animate)
{
StudioFrameAdvance ( );
DispatchAnimEvents( this );
}
if (IsSequenceFinished() && !SequenceLoops())
{
// ResetSequenceInfo();
// hack to avoid reloading model every frame
m_flAnimTime = gpGlobals->curtime;
m_flPlaybackRate = 1.0;
m_bSequenceFinished = false;
m_flLastEventCheck = 0;
m_flCycle = 0;
if (!m_animate)
m_flPlaybackRate = 0.0; // FIX: don't reset framerate
}
}
//
// CyclerUse - starts a rotation trend
//
void CCycler::Use ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{
m_animate = !m_animate;
if (m_animate)
m_flPlaybackRate = 1.0;
else
m_flPlaybackRate = 0.0;
}
//-----------------------------------------------------------------------------
// Purpose: Changes sequences when hurt.
//-----------------------------------------------------------------------------
int CCycler::OnTakeDamage( const CTakeDamageInfo &info )
{
if (m_animate)
{
int nSequence = GetSequence() + 1;
if ( !IsValidSequence(nSequence) )
{
nSequence = 0;
}
ResetSequence( nSequence );
m_flCycle = 0;
}
else
{
m_flPlaybackRate = 1.0;
StudioFrameAdvance ();
m_flPlaybackRate = 0;
Msg( "sequence: %d, frame %.0f\n", GetSequence(), m_flCycle.Get() );
}
return 0;
}
//-----------------------------------------------------------------------------
// Purpose: Input that sets the sequence of the cycler
//-----------------------------------------------------------------------------
void CCycler::InputSetSequence( inputdata_t &inputdata )
{
if (m_animate)
{
// Legacy support: Try it as a number, and support '0'
const char *sChar = inputdata.value.String();
int iSeqNum = atoi( sChar );
if ( !iSeqNum && sChar[0] != '0' )
{
// Treat it as a sequence name
ResetSequence( LookupSequence( sChar ) );
}
else
{
ResetSequence( iSeqNum );
}
if (m_flPlaybackRate == 0.0)
{
ResetSequence( 0 );
}
m_flCycle = 0;
}
}
// FIXME: this doesn't work anymore, and hasn't for a while now.
class CWeaponCycler : public CBaseCombatWeapon
{
DECLARE_DATADESC();
public:
DECLARE_CLASS( CWeaponCycler, CBaseCombatWeapon );
DECLARE_SERVERCLASS();
void Spawn( void );
void PrimaryAttack( void );
void SecondaryAttack( void );
bool Deploy( void );
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL );
string_t m_iszModel;
int m_iModel;
};
IMPLEMENT_SERVERCLASS_ST(CWeaponCycler, DT_WeaponCycler)
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( cycler_weapon, CWeaponCycler );
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CWeaponCycler )
DEFINE_FIELD( m_iszModel, FIELD_STRING ),
DEFINE_FIELD( m_iModel, FIELD_INTEGER ),
END_DATADESC()
void CWeaponCycler::Spawn( )
{
SetSolid( SOLID_BBOX );
AddSolidFlags( FSOLID_NOT_STANDABLE );
SetMoveType( MOVETYPE_NONE );
PrecacheModel( STRING( GetModelName() ) );
SetModel( STRING( GetModelName() ) );
m_iszModel = GetModelName();
m_iModel = GetModelIndex();
UTIL_SetSize(this, Vector(-16, -16, 0), Vector(16, 16, 16));
SetTouch( &CWeaponCycler::DefaultTouch );
}
bool CWeaponCycler::Deploy( )
{
CBaseCombatCharacter *pOwner = GetOwner();
if (pOwner)
{
pOwner->m_flNextAttack = gpGlobals->curtime + 1.0;
SendWeaponAnim( 0 );
m_iClip1 = 0;
m_iClip2 = 0;
return true;
}
return false;
}
bool CWeaponCycler::Holster( CBaseCombatWeapon *pSwitchingTo )
{
CBaseCombatCharacter *pOwner = GetOwner();
if (pOwner)
{
pOwner->m_flNextAttack = gpGlobals->curtime + 0.5;
}
return true;
}
void CWeaponCycler::PrimaryAttack()
{
SendWeaponAnim( GetSequence() );
m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
}
void CWeaponCycler::SecondaryAttack( void )
{
float flFrameRate;
int nSequence = (GetSequence() + 1) % 8;
// BUG: Why do we set this here and then set to zero right after?
SetModelIndex( m_iModel );
flFrameRate = 0.0;
SetModelIndex( 0 );
if (flFrameRate == 0.0)
{
nSequence = 0;
}
SetSequence( nSequence );
SendWeaponAnim( nSequence );
m_flNextSecondaryAttack = gpGlobals->curtime + 0.3;
}
// Flaming Wreakage
class CWreckage : public CAI_BaseNPC
{
public:
DECLARE_CLASS( CWreckage, CAI_BaseNPC );
DECLARE_DATADESC();
void Spawn( void );
void Precache( void );
void Think( void );
float m_flStartTime;
float m_flDieTime;
};
BEGIN_DATADESC( CWreckage )
DEFINE_FIELD( m_flStartTime, FIELD_TIME ),
DEFINE_FIELD( m_flDieTime, FIELD_TIME ),
END_DATADESC()
LINK_ENTITY_TO_CLASS( cycler_wreckage, CWreckage );
void CWreckage::Spawn( void )
{
SetSolid( SOLID_NONE );
SetMoveType( MOVETYPE_NONE );
m_takedamage = 0;
SetCycle( 0 );
SetNextThink( gpGlobals->curtime + 0.1f );
if (GetModelName() != NULL_STRING)
{
PrecacheModel( STRING( GetModelName() ) );
SetModel( STRING( GetModelName() ) );
}
m_flStartTime = gpGlobals->curtime;
}
void CWreckage::Precache( )
{
if ( GetModelName() != NULL_STRING )
PrecacheModel( STRING( GetModelName() ) );
}
void CWreckage::Think( void )
{
StudioFrameAdvance( );
SetNextThink( gpGlobals->curtime + 0.2 );
if (m_flDieTime)
{
if (m_flDieTime < gpGlobals->curtime)
{
UTIL_Remove( this );
return;
}
else if (random->RandomFloat( 0, m_flDieTime - m_flStartTime ) > m_flDieTime - gpGlobals->curtime)
{
return;
}
}
Vector vecSrc;
CollisionProp()->RandomPointInBounds( vec3_origin, Vector(1, 1, 1), &vecSrc );
CPVSFilter filter( vecSrc );
te->Smoke( filter, 0.0,
&vecSrc, g_sModelIndexSmoke,
random->RandomFloat(0,4.9) + 5.0,
random->RandomInt(0, 3) + 8 );
}
// BlendingCycler
// Used to demonstrate animation blending
class CBlendingCycler : public CCycler
{
DECLARE_DATADESC();
public:
DECLARE_CLASS( CBlendingCycler, CCycler );
void Spawn( void );
bool KeyValue( const char *szKeyName, const char *szValue );
void Think( void );
virtual int ObjectCaps( void ) { return (BaseClass::ObjectCaps() | FCAP_DONT_SAVE | FCAP_IMPULSE_USE); }
int m_iLowerBound;
int m_iUpperBound;
int m_iCurrent;
int m_iBlendspeed;
string_t m_iszSequence;
};
LINK_ENTITY_TO_CLASS( cycler_blender, CBlendingCycler );
//---------------------------------------------------------
// Save/Restore
//---------------------------------------------------------
BEGIN_DATADESC( CBlendingCycler )
DEFINE_FIELD( m_iLowerBound, FIELD_INTEGER ),
DEFINE_FIELD( m_iUpperBound, FIELD_INTEGER ),
DEFINE_FIELD( m_iCurrent, FIELD_INTEGER ),
DEFINE_FIELD( m_iBlendspeed, FIELD_INTEGER ),
DEFINE_FIELD( m_iszSequence, FIELD_STRING ),
END_DATADESC()
void CBlendingCycler::Spawn( void )
{
// Remove if it's not blending
if (m_iLowerBound == 0 && m_iUpperBound == 0)
{
UTIL_Remove( this );
return;
}
GenericCyclerSpawn( (char *)STRING( GetModelName() ), Vector(-16,-16,-16), Vector(16,16,16));
if (!m_iBlendspeed)
m_iBlendspeed = 5;
// Initialise Sequence
if (m_iszSequence != NULL_STRING)
{
SetSequence( LookupSequence( STRING(m_iszSequence) ) );
}
m_iCurrent = m_iLowerBound;
}
bool CBlendingCycler::KeyValue( const char *szKeyName, const char *szValue )
{
if (FStrEq(szKeyName, "lowboundary"))
{
m_iLowerBound = atoi(szValue);
}
else if (FStrEq(szKeyName, "highboundary"))
{
m_iUpperBound = atoi(szValue);
}
else if (FStrEq(szKeyName, "blendspeed"))
{
m_iBlendspeed = atoi(szValue);
}
else if (FStrEq(szKeyName, "blendsequence"))
{
m_iszSequence = AllocPooledString(szValue);
}
else
return BaseClass::KeyValue( szKeyName, szValue );
return true;
}
// Blending Cycler think
void CBlendingCycler::Think( void )
{
SetNextThink( gpGlobals->curtime + 0.1f );
// Move
m_iCurrent += m_iBlendspeed;
if ( (m_iCurrent > m_iUpperBound) || (m_iCurrent < m_iLowerBound) )
m_iBlendspeed = m_iBlendspeed * -1;
// Set blend
SetPoseParameter( 0, m_iCurrent );
Msg( "Current Blend: %d\n", m_iCurrent );
if (IsSequenceFinished() && !SequenceLoops())
{
// ResetSequenceInfo();
// hack to avoid reloading model every frame
m_flAnimTime = gpGlobals->curtime;
m_flPlaybackRate = 1.0;
m_bSequenceFinished = false;
m_flLastEventCheck = 0;
m_flCycle = 0;
if (!m_animate)
{
m_flPlaybackRate = 0.0; // FIX: don't reset framerate
}
}
}