|
|
//========= Copyright � 1996-2005, 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 int 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 (GetPlaybackRate() == 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
} } }
|