halflife-photomode/dlls/monstermaker.cpp

290 lines
7.9 KiB
C++

/***
*
* Copyright (c) 1996-2001, 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
//=========================================================
// MonsterMaker - this ent creates monsters during the game.
//=========================================================
class CMonsterMaker : public CBaseMonster
{
public:
void Spawn() override;
void Precache() override;
bool KeyValue(KeyValueData* pkvd) override;
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 DeathNotice(entvars_t* pevChild) override; // monster maker children use this to tell the monster maker that they have died.
void MakeMonster();
bool Save(CSave& save) override;
bool Restore(CRestore& restore) override;
static TYPEDESCRIPTION m_SaveData[];
string_t m_iszMonsterClassname; // classname of the monster(s) that will be created.
int m_cNumMonsters; // max number of monsters this ent can create
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.
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?
};
LINK_ENTITY_TO_CLASS(monstermaker, CMonsterMaker);
TYPEDESCRIPTION CMonsterMaker::m_SaveData[] =
{
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),
};
IMPLEMENT_SAVERESTORE(CMonsterMaker, CBaseMonster);
bool CMonsterMaker::KeyValue(KeyValueData* pkvd)
{
if (FStrEq(pkvd->szKeyName, "monstercount"))
{
m_cNumMonsters = atoi(pkvd->szValue);
return true;
}
else if (FStrEq(pkvd->szKeyName, "m_imaxlivechildren"))
{
m_iMaxLiveChildren = atoi(pkvd->szValue);
return true;
}
else if (FStrEq(pkvd->szKeyName, "monstertype"))
{
m_iszMonsterClassname = ALLOC_STRING(pkvd->szValue);
return true;
}
return CBaseMonster::KeyValue(pkvd);
}
void CMonsterMaker::Spawn()
{
pev->solid = SOLID_NOT;
m_cLiveChildren = 0;
Precache();
if (!FStringNull(pev->targetname))
{
if ((pev->spawnflags & SF_MONSTERMAKER_CYCLIC) != 0)
{
SetUse(&CMonsterMaker::CyclicUse); // drop one monster each time we fire
}
else
{
SetUse(&CMonsterMaker::ToggleUse); // so can be turned on/off
}
if (FBitSet(pev->spawnflags, SF_MONSTERMAKER_START_ON))
{ // start making monsters as soon as monstermaker spawns
m_fActive = true;
SetThink(&CMonsterMaker::MakerThink);
}
else
{ // wait to be activated.
m_fActive = false;
SetThink(&CMonsterMaker::SUB_DoNothing);
}
}
else
{ // no targetname, just start.
pev->nextthink = gpGlobals->time + m_flDelay;
m_fActive = true;
SetThink(&CMonsterMaker::MakerThink);
}
if (m_cNumMonsters == 1)
{
m_fFadeChildren = false;
}
else
{
m_fFadeChildren = true;
}
m_flGround = 0;
}
void CMonsterMaker::Precache()
{
CBaseMonster::Precache();
UTIL_PrecacheOther(STRING(m_iszMonsterClassname));
}
//=========================================================
// MakeMonster- this is the code that drops the monster
//=========================================================
void CMonsterMaker::MakeMonster()
{
edict_t* pent;
entvars_t* pevCreate;
if (m_iMaxLiveChildren > 0 && m_cLiveChildren >= m_iMaxLiveChildren)
{ // not allowed to make a new one yet. Too many live ones out right now.
return;
}
if (0 == m_flGround)
{
// set altitude. Now that I'm activated, any breakables, etc should be out from under me.
TraceResult tr;
UTIL_TraceLine(pev->origin, pev->origin - Vector(0, 0, 2048), ignore_monsters, ENT(pev), &tr);
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];
int count = UTIL_EntitiesInBox(pList, 2, mins, maxs, FL_CLIENT | FL_MONSTER);
if (0 != count)
{
// don't build a stack of monsters!
return;
}
pent = CREATE_NAMED_ENTITY(m_iszMonsterClassname);
if (FNullEnt(pent))
{
ALERT(at_console, "NULL Ent in MonsterMaker!\n");
return;
}
// If I have a target, fire!
if (!FStringNull(pev->target))
{
// delay already overloaded for this entity, so can't call SUB_UseTargets()
FireTargets(STRING(pev->target), this, this, USE_TOGGLE, 0);
}
pevCreate = VARS(pent);
pevCreate->origin = pev->origin;
pevCreate->angles = pev->angles;
SetBits(pevCreate->spawnflags, SF_MONSTER_FALL_TO_GROUND);
// Children hit monsterclip brushes
if ((pev->spawnflags & SF_MONSTERMAKER_MONSTERCLIP) != 0)
SetBits(pevCreate->spawnflags, SF_MONSTER_HITMONSTERCLIP);
DispatchSpawn(ENT(pevCreate));
pevCreate->owner = edict();
if (!FStringNull(pev->netname))
{
// if I have a netname (overloaded), give the child monster that name as a targetname
pevCreate->targetname = pev->netname;
}
m_cLiveChildren++; // count this monster
m_cNumMonsters--;
if (m_cNumMonsters == 0)
{
// 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.
//=========================================================
void CMonsterMaker::CyclicUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
{
MakeMonster();
}
//=========================================================
// ToggleUse - activates/deactivates the monster maker
//=========================================================
void CMonsterMaker::ToggleUse(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value)
{
if (!ShouldToggle(useType, m_fActive))
return;
if (m_fActive)
{
m_fActive = false;
SetThink(NULL);
}
else
{
m_fActive = true;
SetThink(&CMonsterMaker::MakerThink);
}
pev->nextthink = gpGlobals->time;
}
//=========================================================
// MakerThink - creates a new monster every so often
//=========================================================
void CMonsterMaker::MakerThink()
{
pev->nextthink = gpGlobals->time + m_flDelay;
MakeMonster();
}
//=========================================================
//=========================================================
void CMonsterMaker::DeathNotice(entvars_t* pevChild)
{
// ok, we've gotten the deathnotice from our child, now clear out its owner if we don't want it to fade.
m_cLiveChildren--;
if (!m_fFadeChildren)
{
pevChild->owner = NULL;
}
}