Fixed wrong classname for Voltigore and Baby Voltigore.

Fixed unknown classnames showing as '}'.
Remove monster respawn.
Add 'monstermaker' entity.
This commit is contained in:
Giegue
2023-02-20 14:08:35 -03:00
parent 0622ccc638
commit 8d28e23e16
9 changed files with 376 additions and 80 deletions

View File

@@ -601,7 +601,6 @@ template <class T> T * CreateClassPtr( T *a )
// store the class pointer in the array here!!! // store the class pointer in the array here!!!
monsters[monster_index].monster_index = edict_index; monsters[monster_index].monster_index = edict_index;
monsters[monster_index].monster_pent = temp_edict; monsters[monster_index].monster_pent = temp_edict;
monsters[monster_index].respawn_index = -1;
monsters[monster_index].pMonster = (CMBaseMonster *)a; monsters[monster_index].pMonster = (CMBaseMonster *)a;
// get the private data // get the private data

35
src/dlls/cmbaseextra.h Normal file
View File

@@ -0,0 +1,35 @@
#ifndef BASEEXTRA_H
#define BASEEXTRA_H
// any extra entities created in this project that aren't
// monsters will go here
//=========================================================
// MonsterMaker - this ent creates monsters during the game.
//=========================================================
class CMMonsterMaker : public CMBaseMonster
{
public:
void Spawn( void );
void Precache( void );
void KeyValue( KeyValueData* pkvd);
void EXPORT ToggleUse ( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value );
void EXPORT CyclicUse ( edict_t *pActivator, edict_t *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.
void MakeMonster( void );
int m_iMonsterIndex;// index of the monster(s) that will be created.
int m_cNumMonsters;// max number of monsters this ent can create
int m_iMaxLiveChildren;// max number of monsters that this maker may have out at one time.
int m_cLiveChildren;// how many monsters made by this monster maker that are currently alive
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?
};
#endif // BASEEXTRA_H

View File

@@ -580,7 +580,7 @@ void CMBaseMonster::CallGibMonster( void )
if (pev->health < -99) if (pev->health < -99)
{ {
pev->health = 0; pev->health = 0;
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
} }
if ( ShouldFadeOnDeath() && !fade ) if ( ShouldFadeOnDeath() && !fade )
@@ -618,14 +618,11 @@ void CMBaseMonster :: Killed( entvars_t *pevAttacker, int iGib )
SetConditions( bits_COND_LIGHT_DAMAGE ); SetConditions( bits_COND_LIGHT_DAMAGE );
// tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality. // tell owner ( if any ) that we're dead.This is mostly for MonsterMaker functionality.
/*jlb monstermaker
CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner); CMBaseEntity *pOwner = CMBaseEntity::Instance(pev->owner);
if ( pOwner ) if ( pOwner )
{ {
//jlb it crashes here sometimes!!!
pOwner->DeathNotice( pev ); pOwner->DeathNotice( pev );
} }
jlb*/
if ( ShouldGibMonster( iGib ) ) if ( ShouldGibMonster( iGib ) )
{ {
@@ -642,7 +639,7 @@ jlb*/
if (pev->health < -99) if (pev->health < -99)
{ {
pev->health = 0; pev->health = 0;
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
} }
//pev->enemy = ENT( pevAttacker );//why? (sjb) //pev->enemy = ENT( pevAttacker );//why? (sjb)
@@ -894,8 +891,8 @@ int CMBaseMonster :: TakeDamage( entvars_t *pevInflictor, entvars_t *pevAttacker
// do the damage // do the damage
pev->health -= flTake; pev->health -= flTake;
if (pev->flags & FL_MONSTER) if (pev->flags & FL_MONSTER)
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
// HACKHACK Don't kill monsters in a script. Let them break their scripts first // HACKHACK Don't kill monsters in a script. Let them break their scripts first
if ( m_MonsterState == MONSTERSTATE_SCRIPT ) if ( m_MonsterState == MONSTERSTATE_SCRIPT )
@@ -1005,13 +1002,13 @@ int CMBaseMonster :: DeadTakeDamage( entvars_t *pevInflictor, entvars_t *pevAtta
if ( pev->health <= flDamage ) if ( pev->health <= flDamage )
{ {
pev->health = -50; pev->health = -50;
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
Killed( pevAttacker, GIB_ALWAYS ); Killed( pevAttacker, GIB_ALWAYS );
return 0; return 0;
} }
// Accumulate corpse gibbing damage, so you can gib with multiple hits // Accumulate corpse gibbing damage, so you can gib with multiple hits
pev->health -= flDamage * 0.1; pev->health -= flDamage * 0.1;
pev->fuser4 = pev->health; pev->fuser4 = pev->health;
} }
return 1; return 1;

View File

@@ -42,6 +42,7 @@
#include "cmbase.h" #include "cmbase.h"
#include "cmbasemonster.h" #include "cmbasemonster.h"
#include "cmbaseextra.h"
#include "monsters.h" #include "monsters.h"
#include "weapons.h" #include "weapons.h"
#include "hornet.h" #include "hornet.h"
@@ -158,14 +159,15 @@ monster_type_t monster_types[]=
"monster_pitdrone", FALSE, "monster_pitdrone", FALSE,
"monster_shockroach", FALSE, "monster_shockroach", FALSE,
"monster_shocktrooper", FALSE, "monster_shocktrooper", FALSE,
"monster_voltigore", FALSE, "monster_alien_voltigore", FALSE,
"monster_baby_voltigore", FALSE, "monster_alien_babyvoltigore", FALSE,
"monster_babygarg", FALSE, // Sven Co-op Monsters "monster_babygarg", FALSE, // Sven Co-op Monsters
"monster_hwgrunt", FALSE, "monster_hwgrunt", FALSE,
"monster_robogrunt", FALSE, "monster_robogrunt", FALSE,
"monster_stukabat", FALSE, "monster_stukabat", FALSE,
"info_node", FALSE, // Nodes "info_node", FALSE, // Nodes
"info_node_air", FALSE, "info_node_air", FALSE,
"monstermaker", FALSE, // Extra entities
"", FALSE "", FALSE
}; };
@@ -209,15 +211,6 @@ int GetMonsterIndex(void)
void FreeMonsterIndex(int index) void FreeMonsterIndex(int index)
{ {
int idx = monsters[index].respawn_index;
if (idx != -1)
{
monster_spawnpoint[idx].need_to_respawn = TRUE;
monster_spawnpoint[idx].respawn_time = gpGlobals->time +
monster_spawnpoint[idx].delay;
}
delete monsters[index].pMonster; delete monsters[index].pMonster;
monsters[index].monster_index = 0; monsters[index].monster_index = 0;
@@ -557,7 +550,7 @@ void check_monster_info( edict_t *pPlayer )
} }
} }
bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_index, int spawnflags, pKVD *keyvalue) edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawnflags, pKVD *keyvalue)
{ {
int monster_index; int monster_index;
edict_t *monster_pent; edict_t *monster_pent;
@@ -567,7 +560,7 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
{ {
//META_CONS("[MONSTER] ERROR: No FREE Monster edicts!"); //META_CONS("[MONSTER] ERROR: No FREE Monster edicts!");
LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!"); LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!");
return TRUE; return NULL;
} }
// was this monster NOT precached? // was this monster NOT precached?
@@ -601,11 +594,12 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
LOG_MESSAGE(PLID, "%s", msg); LOG_MESSAGE(PLID, "%s", msg);
} }
return TRUE; return NULL;
} }
switch (monster_type) switch (monster_type)
{ {
// Monsters
case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL); break; case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL); break;
case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL); break; case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL); break;
case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL); break; case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL); break;
@@ -636,17 +630,17 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
case 27: monsters[monster_index].pMonster = CreateClassPtr((CMHWGrunt *)NULL); break; case 27: monsters[monster_index].pMonster = CreateClassPtr((CMHWGrunt *)NULL); break;
case 28: monsters[monster_index].pMonster = CreateClassPtr((CMRGrunt *)NULL); break; case 28: monsters[monster_index].pMonster = CreateClassPtr((CMRGrunt *)NULL); break;
case 29: monsters[monster_index].pMonster = CreateClassPtr((CMStukabat *)NULL); break; case 29: monsters[monster_index].pMonster = CreateClassPtr((CMStukabat *)NULL); break;
// Extra entities
case 32: monsters[monster_index].pMonster = CreateClassPtr((CMMonsterMaker *)NULL); break;
} }
if (monsters[monster_index].pMonster == NULL) if (monsters[monster_index].pMonster == NULL)
{ {
//META_CONS("[MONSTER] ERROR: Error Creating Monster!" ); //META_CONS("[MONSTER] ERROR: Error Creating Monster!" );
LOG_MESSAGE(PLID, "ERROR: Error Creating Monster!"); LOG_MESSAGE(PLID, "ERROR: Error Creating Monster!");
return TRUE; return NULL;
} }
monsters[monster_index].respawn_index = respawn_index;
monster_pent = ENT(monsters[monster_index].pMonster->pev); monster_pent = ENT(monsters[monster_index].pMonster->pev);
monsters[monster_index].monster_pent = monster_pent; monsters[monster_index].monster_pent = monster_pent;
@@ -673,15 +667,19 @@ bool spawn_monster(int monster_type, Vector origin, Vector angles, int respawn_i
monsters[monster_index].pMonster->Spawn(); monsters[monster_index].pMonster->Spawn();
// Reverse fadecorpse behaviour // Only modify starting spawnflags for monsters, not for entities!
if ( ( spawnflags & SF_MONSTER_FADECORPSE ) ) if ( monster_index <= 29 )
monster_pent->v.spawnflags &= ~SF_MONSTER_FADECORPSE; {
else // Reverse fadecorpse behaviour
monster_pent->v.spawnflags |= SF_MONSTER_FADECORPSE; if ( ( spawnflags & SF_MONSTER_FADECORPSE ) )
monster_pent->v.spawnflags &= ~SF_MONSTER_FADECORPSE;
else
monster_pent->v.spawnflags |= SF_MONSTER_FADECORPSE;
}
monster_pent->v.fuser4 = monster_pent->v.health; // save the original health monster_pent->v.fuser4 = monster_pent->v.health; // save the original health
return FALSE; return monster_pent;
} }
@@ -698,8 +696,7 @@ void check_respawn(void)
for (int index=0; index < monster_spawn_count; index++) for (int index=0; index < monster_spawn_count; index++)
{ {
if (monster_spawnpoint[index].need_to_respawn && if (monster_spawnpoint[index].need_to_respawn)
(monster_spawnpoint[index].respawn_time <= gpGlobals->time))
{ {
monster_spawnpoint[index].need_to_respawn = FALSE; monster_spawnpoint[index].need_to_respawn = FALSE;
@@ -713,12 +710,10 @@ void check_respawn(void)
keyvalue = monster_spawnpoint[index].keyvalue; keyvalue = monster_spawnpoint[index].keyvalue;
if (spawn_monster(monster_type, origin, angles, index, spawnflags, keyvalue)) if (spawn_monster(monster_type, origin, angles, spawnflags, keyvalue) == NULL)
{ {
// spawn_monster failed, retry again after delay... // spawn_monster failed
monster_spawnpoint[index].need_to_respawn = TRUE; ALERT( at_error, "Failed to spawn %s at origin %f %f %f\n", monster_types[monster_type].name, origin.x, origin.y, origin.z );
monster_spawnpoint[index].respawn_time = gpGlobals->time +
monster_spawnpoint[index].delay;
} }
} }
} }
@@ -886,7 +881,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0) if (monster_angle.y < 0)
monster_angle.y += 360; monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return; return;
} }
@@ -914,9 +909,9 @@ void MonsterCommand(void)
if (monster_angle.y < 0) if (monster_angle.y < 0)
monster_angle.y += 360; monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return; return;
} }
} }
@@ -942,7 +937,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0) if (monster_angle.y < 0)
monster_angle.y += 360; monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return; return;
} }
@@ -969,7 +964,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0) if (monster_angle.y < 0)
monster_angle.y += 360; monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return; return;
} }
@@ -996,7 +991,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0) if (monster_angle.y < 0)
monster_angle.y += 360; monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return; return;
} }
@@ -1023,7 +1018,7 @@ void MonsterCommand(void)
if (monster_angle.y < 0) if (monster_angle.y < 0)
monster_angle.y += 360; monster_angle.y += 360;
spawn_monster(monster_type, v_src, monster_angle, -1, spawnflags, NULL); spawn_monster(monster_type, v_src, monster_angle, spawnflags, NULL);
return; return;
} }
@@ -1294,8 +1289,9 @@ void mmDispatchTouch( edict_t *pentTouched, edict_t *pentOther )
void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
{ {
int index; int index;
CMAGrunt agrunt; // Monsters
CMAGrunt agrunt; // 0
CMApache apache; CMApache apache;
CMBarney barney; CMBarney barney;
CMBigMomma bigmomma; CMBigMomma bigmomma;
@@ -1324,7 +1320,10 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
CMBabyGargantua babygargantua; CMBabyGargantua babygargantua;
CMHWGrunt hwgrunt; CMHWGrunt hwgrunt;
CMRGrunt rgrunt; CMRGrunt rgrunt;
CMStukabat stukabat; CMStukabat stukabat; // 29
// Extra entities
CMMonsterMaker monstermaker; // 32
g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" ); g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" );
@@ -1372,6 +1371,7 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
case 27: hwgrunt.Precache(); break; case 27: hwgrunt.Precache(); break;
case 28: rgrunt.Precache(); break; case 28: rgrunt.Precache(); break;
case 29: stukabat.Precache(); break; case 29: stukabat.Precache(); break;
case 32: monstermaker.Precache(); break;
} }
} }
} }

View File

@@ -94,7 +94,7 @@ void scan_monster_cfg(FILE *fp)
// Now that I think about it this looks slow and bad code >.> // Now that I think about it this looks slow and bad code >.>
// A match is found. What is this? // A match is found. What is this?
if (strncmp(monster_types[mIndex].name, "monster", 7) == 0) if (strncmp(monster_types[mIndex].name, "monster_", 8) == 0)
{ {
// It's a monster, add it to the list // It's a monster, add it to the list
if (monster_spawn_count == MAX_MONSTERS) if (monster_spawn_count == MAX_MONSTERS)
@@ -110,6 +110,22 @@ void scan_monster_cfg(FILE *fp)
monster = TRUE; monster = TRUE;
} }
} }
else if (strcmp(monster_types[mIndex].name, "monstermaker") == 0)
{
// A monster spawner, add it to the list
if (monster_spawn_count == MAX_MONSTERS)
{
// error.exe
LOG_MESSAGE(PLID, "ERROR: can't add monstermaker, reached MAX_MONSTERS!");
badent = TRUE;
}
else
{
monster_spawnpoint[monster_spawn_count].monster = mIndex;
monster_types[mIndex].need_to_precache = TRUE;
monster = TRUE;
}
}
else if (strcmp(monster_types[mIndex].name, "info_node") == 0) else if (strcmp(monster_types[mIndex].name, "info_node") == 0)
{ {
// Normal node // Normal node
@@ -142,7 +158,7 @@ void scan_monster_cfg(FILE *fp)
} }
if (monster_types[mIndex].name[0] == 0) if (monster_types[mIndex].name[0] == 0)
{ {
LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", input); // print conflictive line LOG_MESSAGE(PLID, "ERROR: unknown classname: %s", data[kvd_index-1].value); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!"); LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!");
badent = TRUE; badent = TRUE;
} }
@@ -150,8 +166,8 @@ void scan_monster_cfg(FILE *fp)
else else
{ {
// What are you doing?! // What are you doing?!
LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", input); // print conflictive line LOG_MESSAGE(PLID, "ERROR: BAD ENTITY STRUCTURE! Last line was %s", data[kvd_index-1].key); // print conflictive line
LOG_MESSAGE(PLID, "ERROR: nothing will spawn here!"); LOG_MESSAGE(PLID, "ERROR: classname MUST be the last entry of the entity!" );
badent = TRUE; badent = TRUE;
} }
@@ -192,23 +208,6 @@ void scan_monster_cfg(FILE *fp)
node_spawnpoint[node_spawn_count].origin[2] = z; node_spawnpoint[node_spawn_count].origin[2] = z;
} }
} }
else if (strcmp(data[i].key, "delay") == 0)
{
// ToDo: Remove this keyvalue.
// Monsters spawned directly should not respawn.
if (monster)
{
if (sscanf(data[i].value, "%f", &x) != 1)
{
LOG_MESSAGE(PLID, "ERROR: invalid delay: %s", input); // print conflictive line
// default to 30 seconds
LOG_MESSAGE(PLID, "ERROR: entity respawn frequency will be set to 30 seconds");
x = 30;
}
monster_spawnpoint[monster_spawn_count].delay = x;
}
}
else if (strcmp(data[i].key, "angles") == 0) else if (strcmp(data[i].key, "angles") == 0)
{ {
if (monster) if (monster)
@@ -241,6 +240,30 @@ void scan_monster_cfg(FILE *fp)
monster_spawnpoint[monster_spawn_count].spawnflags = x; monster_spawnpoint[monster_spawn_count].spawnflags = x;
} }
} }
else if (strcmp(data[i].key, "monstertype") == 0)
{
if (monster)
{
// this keyvalue is only valid for monstermaker entity
if (strcmp(data[kvd_index-1].value, "monstermaker") == 0)
{
// process the entity precache here
int mIndex;
for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++)
{
if (strcmp(data[i].value, monster_types[mIndex].name) == 0)
{
monster_types[mIndex].need_to_precache = TRUE;
break; // only one monster at a time
}
}
// pass the keyvalue to the entity
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key);
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value);
}
}
}
else else
{ {
// We do not know this keyvalue, but an specific entity might use it. // We do not know this keyvalue, but an specific entity might use it.
@@ -255,8 +278,7 @@ void scan_monster_cfg(FILE *fp)
if (monster) if (monster)
{ {
// Init monster // Spawn right away
monster_spawnpoint[monster_spawn_count].respawn_time = gpGlobals->time + 0.1; // spawn (nearly) right away
monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE; monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE;
monster_spawn_count++; monster_spawn_count++;
} }

View File

@@ -218,6 +218,10 @@ SOURCE=.\monster_config.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\monstermaker.cpp
# End Source File
# Begin Source File
SOURCE=.\monsters.cpp SOURCE=.\monsters.cpp
# End Source File # End Source File
# Begin Source File # Begin Source File
@@ -330,6 +334,10 @@ SOURCE=.\cmbase.h
# End Source File # End Source File
# Begin Source File # Begin Source File
SOURCE=.\cmbaseextra.h
# End Source File
# Begin Source File
SOURCE=.\cmbasemonster.h SOURCE=.\cmbasemonster.h
# End Source File # End Source File
# Begin Source File # Begin Source File

View File

@@ -27,7 +27,6 @@ typedef struct
int monster_index; int monster_index;
edict_t *monster_pent; edict_t *monster_pent;
bool killed; bool killed;
int respawn_index;
CMBaseMonster *pMonster; CMBaseMonster *pMonster;
} monster_t; } monster_t;
@@ -38,11 +37,9 @@ extern monster_t monsters[MAX_MONSTER_ENTS];
typedef struct { typedef struct {
Vector origin; Vector origin;
Vector angles; Vector angles;
float delay;
unsigned char monster; unsigned char monster;
int spawnflags; int spawnflags;
pKVD *keyvalue; pKVD *keyvalue;
float respawn_time;
bool need_to_respawn; bool need_to_respawn;
} monster_spawnpoint_t; } monster_spawnpoint_t;

241
src/dlls/monstermaker.cpp Normal file
View File

@@ -0,0 +1,241 @@
/***
*
* 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 "cmbase.h"
#include "cmbasemonster.h"
#include "cmbaseextra.h"
#include "monsters.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
extern monster_type_t monster_types[];
extern edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawnflags, pKVD *keyvalue);
// ========================================================
void CMMonsterMaker :: KeyValue( KeyValueData *pkvd )
{
if ( FStrEq(pkvd->szKeyName, "monstercount") )
{
m_cNumMonsters = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if ( FStrEq(pkvd->szKeyName, "m_imaxlivechildren") )
{
m_iMaxLiveChildren = atoi(pkvd->szValue);
pkvd->fHandled = TRUE;
}
else if ( FStrEq(pkvd->szKeyName, "monstertype") )
{
// Process monster_index
int mIndex;
for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++)
{
if (strcmp(pkvd->szValue, monster_types[mIndex].name) == 0)
{
m_iMonsterIndex = mIndex;
break; // grab the first entry we find
}
}
pkvd->fHandled = TRUE;
}
else
CMBaseMonster::KeyValue( pkvd );
}
void CMMonsterMaker :: Spawn( )
{
pev->solid = SOLID_NOT;
m_cLiveChildren = 0;
Precache();
if ( !FStringNull ( pev->targetname ) )
{
if ( pev->spawnflags & SF_MONSTERMAKER_CYCLIC )
{
SetUse ( &CMMonsterMaker::CyclicUse );// drop one monster each time we fire
}
else
{
SetUse ( &CMMonsterMaker::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 ( &CMMonsterMaker::MakerThink );
}
else
{// wait to be activated.
m_fActive = FALSE;
SetThink ( &CMMonsterMaker::SUB_DoNothing );
}
}
else
{// no targetname, just start.
pev->nextthink = gpGlobals->time + m_flDelay;
m_fActive = TRUE;
SetThink ( &CMMonsterMaker::MakerThink );
}
// always fade
m_fFadeChildren = TRUE;
m_flGround = 0;
}
void CMMonsterMaker :: Precache( void )
{
CMBaseMonster::Precache();
// choosen monster is auto-precached
}
//=========================================================
// MakeMonster- this is the code that drops the monster
//=========================================================
void CMMonsterMaker::MakeMonster( void )
{
edict_t *pent;
int createSF = SF_MONSTER_FALL_TO_GROUND;
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 ( !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;
edict_t *pList[2];
int count = UTIL_EntitiesInBox( pList, 2, mins, maxs, FL_CLIENT|FL_MONSTER );
if ( count )
{
// don't build a stack of monsters!
return;
}
// Should children hit monsterclip brushes?
if ( pev->spawnflags & SF_MONSTERMAKER_MONSTERCLIP )
createSF |= SF_MONSTER_HITMONSTERCLIP;
// Attempt to spawn monster
pent = spawn_monster(m_iMonsterIndex, pev->origin, pev->angles, createSF, NULL);
if ( pent == NULL )
{
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->edict(), this->edict(), USE_TOGGLE, 0 );
}
pent->v.owner = edict();
if ( !FStringNull( pev->netname ) )
{
// if I have a netname (overloaded), give the child monster that name as a targetname
pent->v.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 CMMonsterMaker::CyclicUse ( edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value )
{
MakeMonster();
}
//=========================================================
// ToggleUse - activates/deactivates the monster maker
//=========================================================
void CMMonsterMaker :: ToggleUse ( edict_t *pActivator, edict_t *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 ( &CMMonsterMaker::MakerThink );
}
pev->nextthink = gpGlobals->time;
}
//=========================================================
// MakerThink - creates a new monster every so often
//=========================================================
void CMMonsterMaker :: MakerThink ( void )
{
pev->nextthink = gpGlobals->time + m_flDelay;
MakeMonster();
}
//=========================================================
//=========================================================
void CMMonsterMaker :: 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;
}
}

View File

@@ -33,9 +33,6 @@
#define VOLTIGORE_MAX_BEAMS 8 #define VOLTIGORE_MAX_BEAMS 8
#define VOLTIGORE_CLASSNAME "monster_alien_voltigore"
#define VOLTIGORE_BABY_CLASSNAME "monster_alien_babyvoltigore"
#define VOLTIGORE_ZAP_RED 180 #define VOLTIGORE_ZAP_RED 180
#define VOLTIGORE_ZAP_GREEN 16 #define VOLTIGORE_ZAP_GREEN 16
#define VOLTIGORE_ZAP_BLUE 255 #define VOLTIGORE_ZAP_BLUE 255