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.
552 lines
12 KiB
552 lines
12 KiB
/*** |
|
* |
|
* 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 ); |
|
SetNextThink( 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(); |
|
SetNextThink( 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; |
|
SetNextThink( RANDOM_FLOAT( 0.1f, 0.4f ) ); // Load balance these a bit |
|
} |
|
|
|
void CXenHair::Think( void ) |
|
{ |
|
StudioFrameAdvance(); |
|
SetNextThink( 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 ); |
|
SetNextThink( 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(); |
|
SetNextThink( 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, 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(); |
|
SetNextThink( 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 ) |
|
{ |
|
float flInterval = StudioFrameAdvance(); |
|
SetNextThink( 0.1f ); |
|
|
|
#if 0 |
|
DispatchAnimEvents( flInterval ); |
|
|
|
switch( GetActivity() ) |
|
{ |
|
default: |
|
case ACT_IDLE: |
|
break; |
|
} |
|
#endif |
|
}
|
|
|