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