mirror of
https://github.com/FWGS/hlsdk-xash3d
synced 2024-11-28 21:10:21 +01:00
552 lines
12 KiB
C++
552 lines
12 KiB
C++
/***
|
|
*
|
|
* Copyright (c) 1996-2002, Valve LLC. All rights reserved.
|
|
*
|
|
* This product contains software technology licensed from Id
|
|
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
|
|
* All Rights Reserved.
|
|
*
|
|
* Use, distribution, and modification of this source code and/or resulting
|
|
* object code is restricted to non-commercial enhancements to products from
|
|
* Valve LLC. All other use, distribution, or modification is prohibited
|
|
* without written permission from Valve LLC.
|
|
*
|
|
****/
|
|
#include "extdll.h"
|
|
#include "util.h"
|
|
#include "cbase.h"
|
|
#include "animation.h"
|
|
#include "effects.h"
|
|
|
|
#define XEN_PLANT_GLOW_SPRITE "sprites/flare3.spr"
|
|
#define XEN_PLANT_HIDE_TIME 5
|
|
|
|
class CActAnimating : public CBaseAnimating
|
|
{
|
|
public:
|
|
void SetActivity( Activity act );
|
|
inline Activity GetActivity( void ) { return m_Activity; }
|
|
|
|
virtual int ObjectCaps( void ) { return CBaseAnimating::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
|
|
|
|
virtual int Save( CSave &save );
|
|
virtual int Restore( CRestore &restore );
|
|
static TYPEDESCRIPTION m_SaveData[];
|
|
|
|
private:
|
|
Activity m_Activity;
|
|
};
|
|
|
|
TYPEDESCRIPTION CActAnimating::m_SaveData[] =
|
|
{
|
|
DEFINE_FIELD( CActAnimating, m_Activity, FIELD_INTEGER ),
|
|
};
|
|
|
|
IMPLEMENT_SAVERESTORE( CActAnimating, CBaseAnimating )
|
|
|
|
void CActAnimating::SetActivity( Activity act )
|
|
{
|
|
int sequence = LookupActivity( act );
|
|
if( sequence != ACTIVITY_NOT_AVAILABLE )
|
|
{
|
|
pev->sequence = sequence;
|
|
m_Activity = act;
|
|
pev->frame = 0;
|
|
ResetSequenceInfo();
|
|
}
|
|
}
|
|
|
|
class CXenPLight : public CActAnimating
|
|
{
|
|
public:
|
|
void Spawn( void );
|
|
void Precache( void );
|
|
void Touch( CBaseEntity *pOther );
|
|
void Think( void );
|
|
|
|
void LightOn( void );
|
|
void LightOff( void );
|
|
|
|
virtual int Save( CSave &save );
|
|
virtual int Restore( CRestore &restore );
|
|
static TYPEDESCRIPTION m_SaveData[];
|
|
|
|
private:
|
|
CSprite *m_pGlow;
|
|
};
|
|
|
|
LINK_ENTITY_TO_CLASS( xen_plantlight, CXenPLight )
|
|
|
|
TYPEDESCRIPTION CXenPLight::m_SaveData[] =
|
|
{
|
|
DEFINE_FIELD( CXenPLight, m_pGlow, FIELD_CLASSPTR ),
|
|
};
|
|
|
|
IMPLEMENT_SAVERESTORE( CXenPLight, CActAnimating )
|
|
|
|
void CXenPLight::Spawn( void )
|
|
{
|
|
Precache();
|
|
|
|
SET_MODEL( ENT( pev ), "models/light.mdl" );
|
|
pev->movetype = MOVETYPE_NONE;
|
|
pev->solid = SOLID_TRIGGER;
|
|
|
|
UTIL_SetSize( pev, Vector( -80, -80, 0 ), Vector( 80, 80, 32 ) );
|
|
SetActivity( ACT_IDLE );
|
|
pev->nextthink = gpGlobals->time + 0.1f;
|
|
pev->frame = RANDOM_FLOAT( 0, 255 );
|
|
|
|
m_pGlow = CSprite::SpriteCreate( XEN_PLANT_GLOW_SPRITE, pev->origin + Vector( 0, 0, ( pev->mins.z + pev->maxs.z ) * 0.5f ), FALSE );
|
|
m_pGlow->SetTransparency( kRenderGlow, (int)pev->rendercolor.x, (int)pev->rendercolor.y, (int)pev->rendercolor.z, (int)pev->renderamt, (int)pev->renderfx );
|
|
m_pGlow->SetAttachment( edict(), 1 );
|
|
}
|
|
|
|
void CXenPLight::Precache( void )
|
|
{
|
|
PRECACHE_MODEL( "models/light.mdl" );
|
|
PRECACHE_MODEL( XEN_PLANT_GLOW_SPRITE );
|
|
}
|
|
|
|
void CXenPLight::Think( void )
|
|
{
|
|
StudioFrameAdvance();
|
|
pev->nextthink = gpGlobals->time + 0.1f;
|
|
|
|
switch( GetActivity() )
|
|
{
|
|
case ACT_CROUCH:
|
|
if( m_fSequenceFinished )
|
|
{
|
|
SetActivity( ACT_CROUCHIDLE );
|
|
LightOff();
|
|
}
|
|
break;
|
|
case ACT_CROUCHIDLE:
|
|
if( gpGlobals->time > pev->dmgtime )
|
|
{
|
|
SetActivity( ACT_STAND );
|
|
LightOn();
|
|
}
|
|
break;
|
|
case ACT_STAND:
|
|
if( m_fSequenceFinished )
|
|
SetActivity( ACT_IDLE );
|
|
break;
|
|
case ACT_IDLE:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CXenPLight::Touch( CBaseEntity *pOther )
|
|
{
|
|
if( pOther->IsPlayer() )
|
|
{
|
|
pev->dmgtime = gpGlobals->time + XEN_PLANT_HIDE_TIME;
|
|
if( GetActivity() == ACT_IDLE || GetActivity() == ACT_STAND )
|
|
{
|
|
SetActivity( ACT_CROUCH );
|
|
}
|
|
}
|
|
}
|
|
|
|
void CXenPLight::LightOn( void )
|
|
{
|
|
SUB_UseTargets( this, USE_ON, 0 );
|
|
if( m_pGlow )
|
|
m_pGlow->pev->effects &= ~EF_NODRAW;
|
|
}
|
|
|
|
void CXenPLight::LightOff( void )
|
|
{
|
|
SUB_UseTargets( this, USE_OFF, 0 );
|
|
if( m_pGlow )
|
|
m_pGlow->pev->effects |= EF_NODRAW;
|
|
}
|
|
|
|
class CXenHair : public CActAnimating
|
|
{
|
|
public:
|
|
void Spawn( void );
|
|
void Precache( void );
|
|
void Think( void );
|
|
};
|
|
|
|
LINK_ENTITY_TO_CLASS( xen_hair, CXenHair )
|
|
|
|
#define SF_HAIR_SYNC 0x0001
|
|
|
|
void CXenHair::Spawn( void )
|
|
{
|
|
Precache();
|
|
SET_MODEL( edict(), "models/hair.mdl" );
|
|
UTIL_SetSize( pev, Vector( -4, -4, 0 ), Vector( 4, 4, 32 ) );
|
|
pev->sequence = 0;
|
|
|
|
if( !( pev->spawnflags & SF_HAIR_SYNC ) )
|
|
{
|
|
pev->frame = RANDOM_FLOAT( 0, 255 );
|
|
pev->framerate = RANDOM_FLOAT( 0.7f, 1.4f );
|
|
}
|
|
ResetSequenceInfo();
|
|
|
|
pev->solid = SOLID_NOT;
|
|
pev->movetype = MOVETYPE_NONE;
|
|
pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1f, 0.4f ); // Load balance these a bit
|
|
}
|
|
|
|
void CXenHair::Think( void )
|
|
{
|
|
StudioFrameAdvance();
|
|
pev->nextthink = gpGlobals->time + 0.5f;
|
|
}
|
|
|
|
void CXenHair::Precache( void )
|
|
{
|
|
PRECACHE_MODEL( "models/hair.mdl" );
|
|
}
|
|
|
|
class CXenTreeTrigger : public CBaseEntity
|
|
{
|
|
public:
|
|
void Touch( CBaseEntity *pOther );
|
|
static CXenTreeTrigger *TriggerCreate( edict_t *pOwner, const Vector &position );
|
|
};
|
|
|
|
LINK_ENTITY_TO_CLASS( xen_ttrigger, CXenTreeTrigger )
|
|
|
|
CXenTreeTrigger *CXenTreeTrigger::TriggerCreate( edict_t *pOwner, const Vector &position )
|
|
{
|
|
CXenTreeTrigger *pTrigger = GetClassPtr( (CXenTreeTrigger *)NULL );
|
|
pTrigger->pev->origin = position;
|
|
pTrigger->pev->classname = MAKE_STRING( "xen_ttrigger" );
|
|
pTrigger->pev->solid = SOLID_TRIGGER;
|
|
pTrigger->pev->movetype = MOVETYPE_NONE;
|
|
pTrigger->pev->owner = pOwner;
|
|
|
|
return pTrigger;
|
|
}
|
|
|
|
void CXenTreeTrigger::Touch( CBaseEntity *pOther )
|
|
{
|
|
if( pev->owner )
|
|
{
|
|
CBaseEntity *pEntity = CBaseEntity::Instance( pev->owner );
|
|
pEntity->Touch( pOther );
|
|
}
|
|
}
|
|
|
|
#define TREE_AE_ATTACK 1
|
|
|
|
class CXenTree : public CActAnimating
|
|
{
|
|
public:
|
|
void Spawn( void );
|
|
void Precache( void );
|
|
void Touch( CBaseEntity *pOther );
|
|
void Think( void );
|
|
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { Attack(); return 0; }
|
|
void HandleAnimEvent( MonsterEvent_t *pEvent );
|
|
void Attack( void );
|
|
int Classify( void ) { return CLASS_BARNACLE; }
|
|
|
|
virtual int Save( CSave &save );
|
|
virtual int Restore( CRestore &restore );
|
|
static TYPEDESCRIPTION m_SaveData[];
|
|
|
|
static const char *pAttackHitSounds[];
|
|
static const char *pAttackMissSounds[];
|
|
|
|
private:
|
|
CXenTreeTrigger *m_pTrigger;
|
|
};
|
|
|
|
LINK_ENTITY_TO_CLASS( xen_tree, CXenTree )
|
|
|
|
TYPEDESCRIPTION CXenTree::m_SaveData[] =
|
|
{
|
|
DEFINE_FIELD( CXenTree, m_pTrigger, FIELD_CLASSPTR ),
|
|
};
|
|
|
|
IMPLEMENT_SAVERESTORE( CXenTree, CActAnimating )
|
|
|
|
void CXenTree::Spawn( void )
|
|
{
|
|
Precache();
|
|
|
|
SET_MODEL( ENT( pev ), "models/tree.mdl" );
|
|
pev->movetype = MOVETYPE_NONE;
|
|
pev->solid = SOLID_BBOX;
|
|
|
|
pev->takedamage = DAMAGE_YES;
|
|
|
|
UTIL_SetSize( pev, Vector( -30, -30, 0 ), Vector( 30, 30, 188 ) );
|
|
SetActivity( ACT_IDLE );
|
|
pev->nextthink = gpGlobals->time + 0.1f;
|
|
pev->frame = RANDOM_FLOAT( 0, 255 );
|
|
pev->framerate = RANDOM_FLOAT( 0.7f, 1.4f );
|
|
|
|
Vector triggerPosition;
|
|
UTIL_MakeVectorsPrivate( pev->angles, triggerPosition, NULL, NULL );
|
|
triggerPosition = pev->origin + ( triggerPosition * 64 );
|
|
// Create the trigger
|
|
m_pTrigger = CXenTreeTrigger::TriggerCreate( edict(), triggerPosition );
|
|
UTIL_SetSize( m_pTrigger->pev, Vector( -24, -24, 0 ), Vector( 24, 24, 128 ) );
|
|
}
|
|
|
|
const char *CXenTree::pAttackHitSounds[] =
|
|
{
|
|
"zombie/claw_strike1.wav",
|
|
"zombie/claw_strike2.wav",
|
|
"zombie/claw_strike3.wav",
|
|
};
|
|
|
|
const char *CXenTree::pAttackMissSounds[] =
|
|
{
|
|
"zombie/claw_miss1.wav",
|
|
"zombie/claw_miss2.wav",
|
|
};
|
|
|
|
void CXenTree::Precache( void )
|
|
{
|
|
PRECACHE_MODEL( "models/tree.mdl" );
|
|
PRECACHE_MODEL( XEN_PLANT_GLOW_SPRITE );
|
|
PRECACHE_SOUND_ARRAY( pAttackHitSounds );
|
|
PRECACHE_SOUND_ARRAY( pAttackMissSounds );
|
|
}
|
|
|
|
void CXenTree::Touch( CBaseEntity *pOther )
|
|
{
|
|
if( !pOther->IsPlayer() && FClassnameIs( pOther->pev, "monster_bigmomma" ) )
|
|
return;
|
|
|
|
Attack();
|
|
}
|
|
|
|
void CXenTree::Attack( void )
|
|
{
|
|
if( GetActivity() == ACT_IDLE )
|
|
{
|
|
SetActivity( ACT_MELEE_ATTACK1 );
|
|
pev->framerate = RANDOM_FLOAT( 1.0f, 1.4f );
|
|
EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackMissSounds );
|
|
}
|
|
}
|
|
|
|
void CXenTree::HandleAnimEvent( MonsterEvent_t *pEvent )
|
|
{
|
|
switch( pEvent->event )
|
|
{
|
|
case TREE_AE_ATTACK:
|
|
{
|
|
CBaseEntity *pList[8];
|
|
BOOL sound = FALSE;
|
|
int count = UTIL_EntitiesInBox( pList, 8, m_pTrigger->pev->absmin, m_pTrigger->pev->absmax, FL_MONSTER | FL_CLIENT );
|
|
Vector forward;
|
|
|
|
UTIL_MakeVectorsPrivate( pev->angles, forward, NULL, NULL );
|
|
|
|
for( int i = 0; i < count; i++ )
|
|
{
|
|
if( pList[i] != this )
|
|
{
|
|
if( pList[i]->pev->owner != edict() )
|
|
{
|
|
sound = TRUE;
|
|
pList[i]->TakeDamage( pev, pev, 25, DMG_CRUSH | DMG_SLASH );
|
|
pList[i]->pev->punchangle.x = 15;
|
|
pList[i]->pev->velocity = pList[i]->pev->velocity + forward * 100;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( sound )
|
|
{
|
|
EMIT_SOUND_ARRAY_DYN( CHAN_WEAPON, pAttackHitSounds );
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
CActAnimating::HandleAnimEvent( pEvent );
|
|
}
|
|
|
|
void CXenTree::Think( void )
|
|
{
|
|
float flInterval = StudioFrameAdvance();
|
|
pev->nextthink = gpGlobals->time + 0.1f;
|
|
DispatchAnimEvents( flInterval );
|
|
|
|
switch( GetActivity() )
|
|
{
|
|
case ACT_MELEE_ATTACK1:
|
|
if( m_fSequenceFinished )
|
|
{
|
|
SetActivity( ACT_IDLE );
|
|
pev->framerate = RANDOM_FLOAT( 0.6f, 1.4f );
|
|
}
|
|
break;
|
|
default:
|
|
case ACT_IDLE:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// UNDONE: These need to smoke somehow when they take damage
|
|
// Touch behavior?
|
|
// Cause damage in smoke area
|
|
|
|
//
|
|
// Spores
|
|
//
|
|
class CXenSpore : public CActAnimating
|
|
{
|
|
public:
|
|
void Spawn( void );
|
|
void Precache( void );
|
|
void Touch( CBaseEntity *pOther );
|
|
void Think( void );
|
|
int TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker, float flDamage, int bitsDamageType ) { Attack(); return 0; }
|
|
//void HandleAnimEvent( MonsterEvent_t *pEvent );
|
|
void Attack( void ) {}
|
|
|
|
static const char *pModelNames[];
|
|
};
|
|
|
|
class CXenSporeSmall : public CXenSpore
|
|
{
|
|
void Spawn( void );
|
|
};
|
|
|
|
class CXenSporeMed : public CXenSpore
|
|
{
|
|
void Spawn( void );
|
|
};
|
|
|
|
class CXenSporeLarge : public CXenSpore
|
|
{
|
|
void Spawn( void );
|
|
|
|
static const Vector m_hullSizes[];
|
|
};
|
|
|
|
// Fake collision box for big spores
|
|
class CXenHull : public CPointEntity
|
|
{
|
|
public:
|
|
static CXenHull *CreateHull( CBaseEntity *source, const Vector &mins, const Vector &maxs, const Vector &offset );
|
|
int Classify( void ) { return CLASS_BARNACLE; }
|
|
};
|
|
|
|
CXenHull *CXenHull::CreateHull( CBaseEntity *source, const Vector &mins, const Vector &maxs, const Vector &offset )
|
|
{
|
|
CXenHull *pHull = GetClassPtr( (CXenHull *)NULL );
|
|
|
|
UTIL_SetOrigin( pHull->pev, source->pev->origin + offset );
|
|
SET_MODEL( pHull->edict(), STRING( source->pev->model ) );
|
|
pHull->pev->solid = SOLID_BBOX;
|
|
pHull->pev->classname = MAKE_STRING( "xen_hull" );
|
|
pHull->pev->movetype = MOVETYPE_NONE;
|
|
pHull->pev->owner = source->edict();
|
|
UTIL_SetSize( pHull->pev, mins, maxs );
|
|
pHull->pev->renderamt = 0;
|
|
pHull->pev->rendermode = kRenderTransTexture;
|
|
// pHull->pev->effects = EF_NODRAW;
|
|
|
|
return pHull;
|
|
}
|
|
|
|
LINK_ENTITY_TO_CLASS( xen_spore_small, CXenSporeSmall )
|
|
LINK_ENTITY_TO_CLASS( xen_spore_medium, CXenSporeMed )
|
|
LINK_ENTITY_TO_CLASS( xen_spore_large, CXenSporeLarge )
|
|
LINK_ENTITY_TO_CLASS( xen_hull, CXenHull )
|
|
|
|
void CXenSporeSmall::Spawn( void )
|
|
{
|
|
pev->skin = 0;
|
|
CXenSpore::Spawn();
|
|
UTIL_SetSize( pev, Vector( -16, -16, 0 ), Vector( 16, 16, 64) );
|
|
}
|
|
|
|
void CXenSporeMed::Spawn( void )
|
|
{
|
|
pev->skin = 1;
|
|
CXenSpore::Spawn();
|
|
UTIL_SetSize( pev, Vector( -40, -40, 0 ), Vector( 40, 40, 120 ) );
|
|
}
|
|
|
|
// I just eyeballed these -- fill in hulls for the legs
|
|
const Vector CXenSporeLarge::m_hullSizes[] =
|
|
{
|
|
Vector( 90, -25, 0 ),
|
|
Vector( 25, 75, 0 ),
|
|
Vector( -15, -100, 0 ),
|
|
Vector( -90, -35, 0 ),
|
|
Vector( -90, 60, 0 ),
|
|
};
|
|
|
|
void CXenSporeLarge::Spawn( void )
|
|
{
|
|
pev->skin = 2;
|
|
CXenSpore::Spawn();
|
|
UTIL_SetSize( pev, Vector( -48, -48, 110 ), Vector( 48, 48, 240 ) );
|
|
|
|
Vector forward, right;
|
|
|
|
UTIL_MakeVectorsPrivate( pev->angles, forward, right, NULL );
|
|
|
|
// Rotate the leg hulls into position
|
|
for( int i = 0; i < (int)ARRAYSIZE( m_hullSizes ); i++ )
|
|
CXenHull::CreateHull( this, Vector( -12, -12, 0 ), Vector( 12, 12, 120 ), ( m_hullSizes[i].x * forward ) + ( m_hullSizes[i].y * right ) );
|
|
}
|
|
|
|
void CXenSpore :: Spawn( void )
|
|
{
|
|
Precache();
|
|
|
|
SET_MODEL( ENT( pev ), pModelNames[pev->skin] );
|
|
pev->movetype = MOVETYPE_NONE;
|
|
pev->solid = SOLID_BBOX;
|
|
pev->takedamage = DAMAGE_YES;
|
|
|
|
//SetActivity( ACT_IDLE );
|
|
pev->sequence = 0;
|
|
pev->frame = RANDOM_FLOAT( 0, 255 );
|
|
pev->framerate = RANDOM_FLOAT( 0.7f, 1.4f );
|
|
ResetSequenceInfo();
|
|
pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1f, 0.4f ); // Load balance these a bit
|
|
}
|
|
|
|
const char *CXenSpore::pModelNames[] =
|
|
{
|
|
"models/fungus(small).mdl",
|
|
"models/fungus.mdl",
|
|
"models/fungus(large).mdl",
|
|
};
|
|
|
|
void CXenSpore::Precache( void )
|
|
{
|
|
PRECACHE_MODEL( pModelNames[pev->skin] );
|
|
}
|
|
|
|
void CXenSpore::Touch( CBaseEntity *pOther )
|
|
{
|
|
}
|
|
|
|
void CXenSpore::Think( void )
|
|
{
|
|
StudioFrameAdvance();
|
|
pev->nextthink = gpGlobals->time + 0.1f;
|
|
#if 0
|
|
DispatchAnimEvents( flInterval );
|
|
|
|
switch( GetActivity() )
|
|
{
|
|
default:
|
|
case ACT_IDLE:
|
|
break;
|
|
}
|
|
#endif
|
|
}
|