Portable Half-Life SDK. GoldSource and Xash3D. Crossplatform.
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.

363 lines
10 KiB

9 years ago
/***
*
* 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.
*
****/
//=========================================================
// Monster Maker - this is an entity that creates monsters
// in the game.
//=========================================================
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "monsters.h"
#include "saverestore.h"
// Monstermaker spawnflags
#define SF_MONSTERMAKER_START_ON 1 // start active ( if has targetname )
#define SF_MONSTERMAKER_CYCLIC 4 // drop one monster every time fired.
#define SF_MONSTERMAKER_MONSTERCLIP 8 // Children are blocked by monsterclip
#define SF_MONSTERMAKER_WARPBALL 16 // Children are made by warpball
9 years ago
//=========================================================
// MonsterMaker - this ent creates monsters during the game.
//=========================================================
class CMonsterMaker : public CBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData* pkvd);
8 years ago
void EXPORT ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT CyclicUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
void EXPORT MakerThink( void );
void DeathNotice( entvars_t *pevChild );// monster maker children use this to tell the monster maker that they have died.
9 years ago
void MakeMonster( void );
8 years ago
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
9 years ago
8 years ago
static TYPEDESCRIPTION m_SaveData[];
9 years ago
string_t m_iszMonsterClassname;// classname of the monster(s) that will be created.
8 years ago
int m_cNumMonsters;// max number of monsters this ent can create
8 years ago
int m_cLiveChildren;// how many monsters made by this monster maker that are currently alive
int m_iMaxLiveChildren;// max number of monsters that this maker may have out at one time.
9 years ago
float m_flGround; // z coord of the ground under me, used to make sure no monsters are under the maker when it drops a new child
BOOL m_fActive;
BOOL m_fFadeChildren;// should we make the children fadeout?
int m_iSpawnType;
int m_iBeamCount;
float m_flSpriteScale;
float m_flSpawnSoundRad;
float m_flSpawnVol;
9 years ago
};
LINK_ENTITY_TO_CLASS( monstermaker, CMonsterMaker )
9 years ago
TYPEDESCRIPTION CMonsterMaker::m_SaveData[] =
9 years ago
{
DEFINE_FIELD( CMonsterMaker, m_iszMonsterClassname, FIELD_STRING ),
DEFINE_FIELD( CMonsterMaker, m_cNumMonsters, FIELD_INTEGER ),
DEFINE_FIELD( CMonsterMaker, m_cLiveChildren, FIELD_INTEGER ),
DEFINE_FIELD( CMonsterMaker, m_flGround, FIELD_FLOAT ),
DEFINE_FIELD( CMonsterMaker, m_iMaxLiveChildren, FIELD_INTEGER ),
DEFINE_FIELD( CMonsterMaker, m_fActive, FIELD_BOOLEAN ),
DEFINE_FIELD( CMonsterMaker, m_fFadeChildren, FIELD_BOOLEAN ),
DEFINE_FIELD( CMonsterMaker, m_iSpawnType, FIELD_INTEGER ),
DEFINE_FIELD( CMonsterMaker, m_iBeamCount, FIELD_INTEGER ),
DEFINE_FIELD( CMonsterMaker, m_flSpriteScale, FIELD_FLOAT ),
DEFINE_FIELD( CMonsterMaker, m_flSpawnSoundRad, FIELD_FLOAT ),
DEFINE_FIELD( CMonsterMaker, m_flSpawnVol, FIELD_FLOAT ),
9 years ago
};
IMPLEMENT_SAVERESTORE( CMonsterMaker, CBaseMonster )
9 years ago
8 years ago
void CMonsterMaker::KeyValue( KeyValueData *pkvd )
9 years ago
{
8 years ago
if( FStrEq( pkvd->szKeyName, "monstercount" ) )
9 years ago
{
8 years ago
m_cNumMonsters = atoi( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "m_imaxlivechildren" ) )
9 years ago
{
8 years ago
m_iMaxLiveChildren = atoi( pkvd->szValue );
9 years ago
pkvd->fHandled = TRUE;
}
8 years ago
else if( FStrEq( pkvd->szKeyName, "monstertype" ) )
9 years ago
{
m_iszMonsterClassname = ALLOC_STRING( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "spawntype" ) )
{
m_iSpawnType = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "beamcount" ) )
{
m_iBeamCount = atoi( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "spritescale" ) )
{
m_flSpriteScale = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "spawnsoundrad" ) )
{
switch( atoi( pkvd->szValue ) )
{
case 3:
m_flSpawnSoundRad = ATTN_NONE;
break;
case 2:
m_flSpawnSoundRad = ATTN_NORM;
break;
case 1:
m_flSpawnSoundRad = ATTN_IDLE;
break;
default:
m_flSpawnSoundRad = ATTN_STATIC;
break;
}
pkvd->fHandled = TRUE;
}
else if( FStrEq( pkvd->szKeyName, "spawnvol" ) )
{
m_flSpawnVol = atof( pkvd->szValue );
pkvd->fHandled = TRUE;
}
9 years ago
CBaseMonster::KeyValue( pkvd );
}
8 years ago
void CMonsterMaker::Spawn()
9 years ago
{
pev->solid = SOLID_NOT;
m_cLiveChildren = 0;
Precache();
8 years ago
if( !FStringNull( pev->targetname ) )
9 years ago
{
8 years ago
if( pev->spawnflags & SF_MONSTERMAKER_CYCLIC )
9 years ago
{
SetUse( &CMonsterMaker::CyclicUse );// drop one monster each time we fire
}
else
{
SetUse( &CMonsterMaker::ToggleUse );// so can be turned on/off
}
8 years ago
if( FBitSet( pev->spawnflags, SF_MONSTERMAKER_START_ON ) )
{
// start making monsters as soon as monstermaker spawns
9 years ago
m_fActive = TRUE;
SetThink( &CMonsterMaker::MakerThink );
}
else
{
// wait to be activated.
9 years ago
m_fActive = FALSE;
SetThink( &CBaseEntity::SUB_DoNothing );
}
}
else
{
// no targetname, just start.
pev->nextthink = gpGlobals->time + m_flDelay;
m_fActive = TRUE;
SetThink( &CMonsterMaker::MakerThink );
9 years ago
}
8 years ago
if( m_cNumMonsters == 1 )
9 years ago
{
m_fFadeChildren = FALSE;
}
else
{
m_fFadeChildren = TRUE;
}
m_flGround = 0;
}
8 years ago
void CMonsterMaker::Precache( void )
9 years ago
{
if( FBitSet( pev->spawnflags, SF_MONSTERMAKER_WARPBALL ) )
UTIL_PrecacheOther( "env_spawnereffect" );
9 years ago
CBaseMonster::Precache();
UTIL_PrecacheOther( STRING( m_iszMonsterClassname ) );
}
//=========================================================
// MakeMonster- this is the code that drops the monster
//=========================================================
void CMonsterMaker::MakeMonster( void )
{
edict_t *pent;
8 years ago
entvars_t *pevCreate;
9 years ago
8 years ago
if( m_iMaxLiveChildren > 0 && m_cLiveChildren >= m_iMaxLiveChildren )
{
// not allowed to make a new one yet. Too many live ones out right now.
9 years ago
return;
}
8 years ago
if( !m_flGround )
9 years ago
{
// set altitude. Now that I'm activated, any breakables, etc should be out from under me.
TraceResult tr;
8 years ago
UTIL_TraceLine( pev->origin, pev->origin - Vector( 0, 0, 2048 ), ignore_monsters, ENT( pev ), &tr );
9 years ago
m_flGround = tr.vecEndPos.z;
}
Vector mins = pev->origin - Vector( 34, 34, 0 );
Vector maxs = pev->origin + Vector( 34, 34, 0 );
maxs.z = pev->origin.z;
mins.z = m_flGround;
CBaseEntity *pList[2];
8 years ago
int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_CLIENT | FL_MONSTER );
if( count )
9 years ago
{
// don't build a stack of monsters!
return;
}
pent = CREATE_NAMED_ENTITY( m_iszMonsterClassname );
8 years ago
if( FNullEnt( pent ) )
9 years ago
{
ALERT ( at_console, "NULL Ent in MonsterMaker!\n" );
return;
}
8 years ago
9 years ago
// If I have a target, fire!
8 years ago
if( !FStringNull( pev->target ) )
9 years ago
{
// delay already overloaded for this entity, so can't call SUB_UseTargets()
8 years ago
FireTargets( STRING( pev->target ), this, this, USE_TOGGLE, 0 );
9 years ago
}
pevCreate = VARS( pent );
pevCreate->origin = pev->origin;
pevCreate->angles = pev->angles;
if( FBitSet( pev->spawnflags, SF_MONSTERMAKER_WARPBALL ) )
pevCreate->origin.z -= 16;
9 years ago
SetBits( pevCreate->spawnflags, SF_MONSTER_FALL_TO_GROUND );
// Children hit monsterclip brushes
8 years ago
if( pev->spawnflags & SF_MONSTERMAKER_MONSTERCLIP )
9 years ago
SetBits( pevCreate->spawnflags, SF_MONSTER_HITMONSTERCLIP );
DispatchSpawn( ENT( pevCreate ) );
pevCreate->owner = edict();
8 years ago
if( !FStringNull( pev->netname ) )
9 years ago
{
// if I have a netname (overloaded), give the child monster that name as a targetname
pevCreate->targetname = pev->netname;
}
if( FBitSet( pev->spawnflags, SF_MONSTERMAKER_WARPBALL ) )
{
int iLightRad;
float height = ( pevCreate->absmax.z - pevCreate->absmin.z ) * 0.5f;
if( FClassnameIs( pevCreate, "monster_alien_slave" )
|| FClassnameIs( pevCreate, "monster_exp_alien_slave" ) )
iLightRad = 24;
else if( FClassnameIs( pevCreate, "monster_alien_grunt" ))
iLightRad = 32;
else
iLightRad = pevCreate->size.z / 3;
UTIL_CreateWarpball( ENT( pevCreate ), pevCreate->origin + Vector( 0, 0, height ), m_flSpawnVol, m_flSpawnSoundRad, m_iBeamCount, m_iSpawnType, m_flSpriteScale, iLightRad );
}
9 years ago
m_cLiveChildren++;// count this monster
m_cNumMonsters--;
8 years ago
if( m_cNumMonsters == 0 )
9 years ago
{
// Disable this forever. Don't kill it because it still gets death notices
SetThink( NULL );
SetUse( NULL );
}
}
//=========================================================
// CyclicUse - drops one monster from the monstermaker
// each time we call this.
//=========================================================
8 years ago
void CMonsterMaker::CyclicUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
9 years ago
{
MakeMonster();
}
//=========================================================
// ToggleUse - activates/deactivates the monster maker
//=========================================================
8 years ago
void CMonsterMaker::ToggleUse( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
9 years ago
{
8 years ago
if( !ShouldToggle( useType, m_fActive ) )
9 years ago
return;
8 years ago
if( m_fActive )
9 years ago
{
m_fActive = FALSE;
SetThink( NULL );
}
else
{
m_fActive = TRUE;
SetThink( &CMonsterMaker::MakerThink );
}
pev->nextthink = gpGlobals->time;
}
//=========================================================
// MakerThink - creates a new monster every so often
//=========================================================
8 years ago
void CMonsterMaker::MakerThink( void )
9 years ago
{
pev->nextthink = gpGlobals->time + m_flDelay;
MakeMonster();
}
//=========================================================
//=========================================================
8 years ago
void CMonsterMaker::DeathNotice( entvars_t *pevChild )
9 years ago
{
// ok, we've gotten the deathnotice from our child, now clear out its owner if we don't want it to fade.
m_cLiveChildren--;
8 years ago
if( !m_fFadeChildren )
9 years ago
{
pevChild->owner = NULL;
}
}