Files
monstermod-redo-repo-copy/src/dlls/dllapi.cpp
2020-02-22 16:33:00 -03:00

1158 lines
34 KiB
C++

//
// botman's monster - MetaMOD plugin
//
// dllapi.cpp
//
/*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
* This is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this code; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* In addition, as a special exception, the author gives permission to
* link the code of this program with the Half-Life Game Engine ("HL
* Engine") and Modified Game Libraries ("MODs") developed by Valve,
* L.L.C ("Valve"). You must obey the GNU General Public License in all
* respects for all of the code used other than the HL Engine and MODs
* from Valve. If you modify this file, you may extend this exception
* to your version of the file, but you are not obligated to do so. If
* you do not wish to do so, delete this exception statement from your
* version.
*
*/
#include "extdll.h"
#include "dllapi.h"
#include "meta_api.h"
#include "cmbase.h"
#include "cmbasemonster.h"
#include "monsters.h"
#include "weapons.h"
#include "hornet.h"
#include "decals.h"
#include "shake.h"
#include "skill.h"
extern globalvars_t *gpGlobals;
extern enginefuncs_t g_engfuncs;
extern gamedll_funcs_t *gpGamedllFuncs;
extern cvar_t *dllapi_log;
extern cvar_t *monster_spawn;
cvar_t *g_psv_gravity = NULL;
DLL_DECALLIST gDecals[] = {
{ "{shot1", -1 }, // DECAL_GUNSHOT1
{ "{shot2", -1 }, // DECAL_GUNSHOT2
{ "{shot3", -1 }, // DECAL_GUNSHOT3
{ "{shot4", -1 }, // DECAL_GUNSHOT4
{ "{shot5", -1 }, // DECAL_GUNSHOT5
{ "{lambda01", -1 }, // DECAL_LAMBDA1
{ "{lambda02", -1 }, // DECAL_LAMBDA2
{ "{lambda03", -1 }, // DECAL_LAMBDA3
{ "{lambda04", -1 }, // DECAL_LAMBDA4
{ "{lambda05", -1 }, // DECAL_LAMBDA5
{ "{lambda06", -1 }, // DECAL_LAMBDA6
{ "{scorch1", -1 }, // DECAL_SCORCH1
{ "{scorch2", -1 }, // DECAL_SCORCH2
{ "{blood1", -1 }, // DECAL_BLOOD1
{ "{blood2", -1 }, // DECAL_BLOOD2
{ "{blood3", -1 }, // DECAL_BLOOD3
{ "{blood4", -1 }, // DECAL_BLOOD4
{ "{blood5", -1 }, // DECAL_BLOOD5
{ "{blood6", -1 }, // DECAL_BLOOD6
{ "{yblood1", -1 }, // DECAL_YBLOOD1
{ "{yblood2", -1 }, // DECAL_YBLOOD2
{ "{yblood3", -1 }, // DECAL_YBLOOD3
{ "{yblood4", -1 }, // DECAL_YBLOOD4
{ "{yblood5", -1 }, // DECAL_YBLOOD5
{ "{yblood6", -1 }, // DECAL_YBLOOD6
{ "{break1", -1 }, // DECAL_GLASSBREAK1
{ "{break2", -1 }, // DECAL_GLASSBREAK2
{ "{break3", -1 }, // DECAL_GLASSBREAK3
{ "{bigshot1", -1 }, // DECAL_BIGSHOT1
{ "{bigshot2", -1 }, // DECAL_BIGSHOT2
{ "{bigshot3", -1 }, // DECAL_BIGSHOT3
{ "{bigshot4", -1 }, // DECAL_BIGSHOT4
{ "{bigshot5", -1 }, // DECAL_BIGSHOT5
{ "{spit1", -1 }, // DECAL_SPIT1
{ "{spit2", -1 }, // DECAL_SPIT2
{ "{bproof1", -1 }, // DECAL_BPROOF1
{ "{gargstomp", -1 }, // DECAL_GARGSTOMP1, // Gargantua stomp crack
{ "{smscorch1", -1 }, // DECAL_SMALLSCORCH1, // Small scorch mark
{ "{smscorch2", -1 }, // DECAL_SMALLSCORCH2, // Small scorch mark
{ "{smscorch3", -1 }, // DECAL_SMALLSCORCH3, // Small scorch mark
{ "{mommablob", -1 }, // DECAL_MOMMABIRTH // BM Birth spray
{ "{mommablob", -1 }, // DECAL_MOMMASPLAT // BM Mortar spray?? need decal
};
monster_type_t monster_types[]=
{
"agrunt", FALSE,
"apache", FALSE,
"barney", FALSE,
"bigmomma", FALSE,
"bullsquid", FALSE,
"controller", FALSE,
"hassassin", FALSE,
"headcrab", FALSE,
"hgrunt", FALSE,
"houndeye", FALSE,
"islave", FALSE,
"scientist", FALSE,
"snark", FALSE,
"zombie", FALSE,
"", FALSE};
monster_t monsters[MAX_MONSTER_ENTS];
int monster_ents_used = 0;
monster_spawnpoint_t monster_spawnpoint[MAX_MONSTERS];
int monster_spawn_count = 0;
float check_respawn_time;
bool process_monster_cfg(void);
bool process_monster_precache_cfg(void);
int GetMonsterIndex(void)
{
int monster_index = -1;
for (int index = 0; index < MAX_MONSTER_ENTS; index++)
{
if (monsters[index].monster_pent == 0)
{
monster_index = index;
break;
}
}
if (monster_index == -1)
return -1;
if (monster_index >= monster_ents_used)
monster_ents_used = monster_index + 1; // monster index is 0 based
return monster_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;
monsters[index].monster_index = 0;
monsters[index].monster_pent = NULL;
monsters[index].killed = FALSE;
monsters[index].pMonster = NULL;
if (index == monster_ents_used-1)
{
while (monsters[index].monster_index == 0)
{
index--;
monster_ents_used--;
if (monster_ents_used == 0)
break;
}
}
}
void Remove_Entity(edict_t *pEdict)
{
for (int index = 0; index < monster_ents_used; index++)
{
if (monsters[index].monster_pent == pEdict)
{
FreeMonsterIndex(index);
break;
}
}
REMOVE_ENTITY(pEdict);
}
void monster_unload(void)
{
// the plugin is being unloaded, remove any currently spawned monster...
for (int index = 0; index < MAX_MONSTER_ENTS; index++)
{
if (monsters[index].pMonster != NULL)
{
monsters[index].monster_pent->v.flags |= FL_KILLME;
delete monsters[index].pMonster;
monsters[index].monster_index = 0;
monsters[index].monster_pent = NULL;
monsters[index].killed = FALSE;
monsters[index].pMonster = NULL;
}
}
}
void check_monster_hurt(edict_t *pAttacker)
{
int index;
for (index = 0; index < monster_ents_used; index++)
{
if (monsters[index].monster_index)
{
edict_t *pent = (*g_engfuncs.pfnPEntityOfEntIndex)(monsters[index].monster_index);
if (pent)
{
if (pent->v.health < pent->v.fuser4)
{
if (pent->v.takedamage != DAMAGE_NO)
{
TraceResult tr;
Vector vecSrc, vecSpot;
float distance, damage;
// location of attacker and location of enemy...
vecSrc = pAttacker->v.origin + pAttacker->v.view_ofs;
vecSpot = pent->v.origin;
// distance the blood can travel from the body...
distance = (vecSpot - vecSrc).Length() + 100.0f;
// use aiming angles of attacker to trace blood splatter...
UTIL_MakeVectors(pAttacker->v.v_angle);
// start just beyond the attacker's body...
vecSrc = vecSrc + gpGlobals->v_forward * 20;
vecSpot = vecSrc + gpGlobals->v_forward * distance;
// trace a line ignoring enemies body...
UTIL_TraceLine ( vecSrc, vecSpot, dont_ignore_monsters, pent, &tr );
damage = pent->v.fuser4 - pent->v.health;
// restore previous health, then do the damage (again)
pent->v.health = pent->v.fuser4;
ClearMultiDamage( );
monsters[index].pMonster->TraceAttack( VARS(pAttacker), damage, (tr.vecEndPos - vecSrc).Normalize( ), &tr, DMG_BULLET );
ApplyMultiDamage( VARS(pAttacker), VARS(pAttacker) );
}
// save the new current health as previous health...
pent->v.fuser4 = pent->v.health;
}
}
else
{
// the entity no longer exists and we didn't catch it dying
FreeMonsterIndex(index);
}
}
}
}
void check_monster_dead(void)
{
for (int index = 0; index < monster_ents_used; index++)
{
if (monsters[index].monster_index)
{
edict_t *pent = (*g_engfuncs.pfnPEntityOfEntIndex)(monsters[index].monster_index);
if (pent)
{
if (pent->v.flags & FL_KILLME) // func_wall was "killed"
{
if (pent->v.flags & FL_MONSTER) // is this a monster?
{
if (monsters[index].killed == FALSE)
{
pent->v.flags &= ~FL_KILLME; // clear FL_KILLME bit
pent->v.deadflag = DEAD_NO; // bring back to life
monsters[index].pMonster->Killed(VARS(pent), 0);
monsters[index].killed = TRUE;
}
}
else // normal entity
{
FreeMonsterIndex(index);
}
}
}
else
{
FreeMonsterIndex(index);
}
}
}
}
bool spawn_monster(int monster_type, Vector origin, float angle, int respawn_index)
{
int monster_index;
edict_t *monster_pent;
if ((monster_index = GetMonsterIndex()) == -1)
{
META_CONS("[MONSTER] ERROR: No FREE Monster edicts!");
LOG_MESSAGE(PLID, "ERROR: No FREE Monster edicts!");
return TRUE;
}
// was this monster NOT precached?
if (monster_types[monster_type].need_to_precache == FALSE)
{
char msg[256];
META_CONS("[MONSTER] ERROR: You can't spawn monster %s since it wasn't precached!",
monster_types[monster_type].name);
META_CONS("[MONSTER] valid precached monster names are:");
msg[0] = 0;
for (int index = 0; monster_types[index].name[0]; index++)
{
if (monster_types[index].need_to_precache == TRUE)
{
strcat(msg, monster_types[index].name);
strcat(msg, " ");
if (strlen(msg) > 60)
{
META_CONS("[MONSTER] %s", msg);
msg[0] = 0;
}
}
}
if (msg[0])
META_CONS("[MONSTER] %s", msg);
return TRUE;
}
switch (monster_type)
{
case 0: monsters[monster_index].pMonster = CreateClassPtr((CMAGrunt *)NULL);
break;
case 1: monsters[monster_index].pMonster = CreateClassPtr((CMApache *)NULL);
break;
case 2: monsters[monster_index].pMonster = CreateClassPtr((CMBarney *)NULL);
break;
case 3: monsters[monster_index].pMonster = CreateClassPtr((CMBigMomma *)NULL);
break;
case 4: monsters[monster_index].pMonster = CreateClassPtr((CMBullsquid *)NULL);
break;
case 5: monsters[monster_index].pMonster = CreateClassPtr((CMController *)NULL);
break;
case 6: monsters[monster_index].pMonster = CreateClassPtr((CMHAssassin *)NULL);
break;
case 7: monsters[monster_index].pMonster = CreateClassPtr((CMHeadCrab *)NULL);
break;
case 8: monsters[monster_index].pMonster = CreateClassPtr((CMHGrunt *)NULL);
break;
case 9: monsters[monster_index].pMonster = CreateClassPtr((CMHoundeye *)NULL);
break;
case 10: monsters[monster_index].pMonster = CreateClassPtr((CMISlave *)NULL);
break;
case 11: monsters[monster_index].pMonster = CreateClassPtr((CMScientist *)NULL);
break;
case 12: monsters[monster_index].pMonster = CreateClassPtr((CMSqueakGrenade *)NULL);
break;
case 13: monsters[monster_index].pMonster = CreateClassPtr((CMZombie *)NULL);
break;
}
if (monsters[monster_index].pMonster == NULL)
{
META_CONS("[MONSTER] ERROR: Error Creating Monster!" );
LOG_MESSAGE(PLID, "ERROR: Error Creating Monster!");
return TRUE;
}
monsters[monster_index].respawn_index = respawn_index;
monster_pent = ENT(monsters[monster_index].pMonster->pev);
monsters[monster_index].monster_pent = monster_pent;
monsters[monster_index].monster_index = (*g_engfuncs.pfnIndexOfEdict)(monster_pent);
monster_pent->v.origin = origin;
monster_pent->v.angles.y = angle;
monsters[monster_index].pMonster->Spawn();
monster_pent->v.spawnflags = SF_MONSTER_FADECORPSE;
monster_pent->v.fuser4 = monster_pent->v.health; // save the original health
return FALSE;
}
void check_respawn(void)
{
int type_index;
int monster_type;
Vector origin;
float angle;
if (!monster_spawn->value)
return; // monster_spawn is turned off, retry again later
for (int index=0; index < monster_spawn_count; index++)
{
if (monster_spawnpoint[index].need_to_respawn &&
(monster_spawnpoint[index].respawn_time <= gpGlobals->time))
{
monster_spawnpoint[index].need_to_respawn = FALSE;
type_index = RANDOM_LONG(0, monster_spawnpoint[index].monster_count-1);
monster_type = monster_spawnpoint[index].monster[type_index];
origin = monster_spawnpoint[index].origin;
angle = monster_spawnpoint[index].angle_min;
if (angle != monster_spawnpoint[index].angle_max)
{
angle = RANDOM_FLOAT(angle, monster_spawnpoint[index].angle_max);
}
if (spawn_monster(monster_type, origin, angle, index))
{
// spawn_monster failed, retry again after delay...
monster_spawnpoint[index].need_to_respawn = TRUE;
monster_spawnpoint[index].respawn_time = gpGlobals->time +
monster_spawnpoint[index].delay;
}
}
}
}
DLL_GLOBAL short g_sModelIndexFireball;// holds the index for the fireball
DLL_GLOBAL short g_sModelIndexSmoke;// holds the index for the smoke cloud
DLL_GLOBAL short g_sModelIndexWExplosion;// holds the index for the underwater explosion
DLL_GLOBAL short g_sModelIndexBubbles;// holds the index for the bubbles model
DLL_GLOBAL short g_sModelIndexBloodDrop;// holds the sprite index for the initial blood
DLL_GLOBAL short g_sModelIndexBloodSpray;// holds the sprite index for splattered blood
DLL_GLOBAL short g_sModelIndexLaser;// holds the index for the laser beam
DLL_GLOBAL const char *g_pModelNameLaser = "sprites/laserbeam.spr";
DLL_GLOBAL short g_sModelIndexLaserDot;// holds the index for the laser beam dot
void world_precache(void)
{
g_sModelIndexFireball = PRECACHE_MODEL ("sprites/zerogxplode.spr");// fireball
g_sModelIndexSmoke = PRECACHE_MODEL ("sprites/steam1.spr");// smoke
g_sModelIndexWExplosion = PRECACHE_MODEL ("sprites/WXplo1.spr");// underwater fireball
g_sModelIndexBubbles = PRECACHE_MODEL ("sprites/bubble.spr");//bubbles
g_sModelIndexBloodSpray = PRECACHE_MODEL ("sprites/bloodspray.spr"); // initial blood
g_sModelIndexBloodDrop = PRECACHE_MODEL ("sprites/blood.spr"); // splattered blood
g_sModelIndexLaser = PRECACHE_MODEL( (char *)g_pModelNameLaser );
g_sModelIndexLaserDot = PRECACHE_MODEL("sprites/laserdot.spr");
PRECACHE_MODEL ("models/w_grenade.mdl");
}
void MonsterCommand(void)
{
int index;
char msg[256];
int monster_type = -1;
if (CMD_ARGC() >= 3)
{
const char *parg1 = CMD_ARGV(1);
// check for a valid monster name...
for (index = 0; monster_types[index].name[0]; index++)
{
if (strcmp(parg1, monster_types[index].name) == 0)
{
monster_type = index;
break;
}
}
if (monster_type != -1)
{
// check for a valid player name or index...
const char *parg2 = CMD_ARGV(2);
int player_index = -1;
edict_t *pPlayer;
const char *player_name;
if (*parg2 == '#') // player index
{
if (sscanf(&parg2[1], "%d", &player_index) != 1)
player_index = -1;
if ((player_index < 1) || (player_index > gpGlobals->maxClients))
{
META_CONS("[MONSTER] invalid player index! (%d to %d allowed)",
1, gpGlobals->maxClients);
player_index = -1;
}
}
else
{
for (index = 1; index <= gpGlobals->maxClients; index++)
{
pPlayer = INDEXENT(index);
if (pPlayer && !pPlayer->free)
{
if (stricmp(STRING(pPlayer->v.netname), parg2) == 0)
{
player_index = index; // found the matching player name
break;
}
}
}
if (player_index == -1)
{
META_CONS("[MONSTER] can't find player named \"%s\"!", parg2);
return;
}
}
if (player_index != -1)
{
pPlayer = INDEXENT(player_index);
if ((pPlayer == NULL) || (pPlayer->free))
{
META_CONS("[MONSTER] player index %d is not a valid player!",
player_index);
return;
}
player_name = STRING(pPlayer->v.netname);
if (player_name[0] == 0)
{
META_CONS("[MONSTER] player index %d is not a valid player!",
player_index);
return;
}
if (!UTIL_IsAlive(pPlayer))
{
META_CONS("[MONSTER] player \"%s\" is not alive or is an observer!",
player_name);
return;
}
TraceResult tr;
Vector origin = pPlayer->v.origin;
Vector view_angle = pPlayer->v.v_angle;
Vector v_src, v_dest;
float monster_angle;
// try to determine the best place to spawn the monster...
view_angle.x = 0; // zero the pitch (level horizontally)
UTIL_MakeVectors(view_angle);
v_src = origin + Vector(0, 0, 20); // up a little bit
v_dest = v_src + gpGlobals->v_forward * 128; // in front of player
UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr);
if (tr.flFraction >= 1.0)
{
v_src = v_dest;
v_dest = v_dest + Vector(0, 0, -200); // down to ground
// try to find the floor...
UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr);
if (tr.flFraction < 1.0) // hit something?
{
monster_angle = view_angle.y + 180.0f; // face the player
if (monster_angle > 360)
monster_angle -= 360;
if (monster_angle < 0)
monster_angle += 360;
spawn_monster(monster_type, v_src, monster_angle, -1);
return;
}
}
v_src = origin + Vector(0, 0, 20); // up a little bit
// diagonally in front and to the left of the player...
v_dest = v_src + gpGlobals->v_forward * 90 + gpGlobals->v_right * -90;
UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr);
if (tr.flFraction >= 1.0)
{
v_src = v_dest;
v_dest = v_dest + Vector(0, 0, -200); // down to ground
// try to find the floor...
UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr);
if (tr.flFraction < 1.0) // hit something?
{
monster_angle = view_angle.y - 135.0f; // face the player
if (monster_angle > 360)
monster_angle -= 360;
if (monster_angle < 0)
monster_angle += 360;
spawn_monster(monster_type, v_src, monster_angle, -1);
return;
}
}
v_src = origin + Vector(0, 0, 20); // up a little bit
// diagonally in front and to the right of the player...
v_dest = v_src + gpGlobals->v_forward * 90 + gpGlobals->v_right * 90;
UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr);
if (tr.flFraction >= 1.0)
{
v_src = v_dest;
v_dest = v_dest + Vector(0, 0, -200); // down to ground
// try to find the floor...
UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr);
if (tr.flFraction < 1.0) // hit something?
{
monster_angle = view_angle.y + 135.0f; // face the player
if (monster_angle > 360)
monster_angle -= 360;
if (monster_angle < 0)
monster_angle += 360;
spawn_monster(monster_type, v_src, monster_angle, -1);
return;
}
}
v_src = origin + Vector(0, 0, 20); // up a little bit
v_dest = v_src + gpGlobals->v_right * 128; // to the right
UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr);
if (tr.flFraction >= 1.0)
{
v_src = v_dest;
v_dest = v_dest + Vector(0, 0, -200); // down to ground
// try to find the floor...
UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr);
if (tr.flFraction < 1.0) // hit something?
{
monster_angle = view_angle.y + 90.0f; // face the player
if (monster_angle > 360)
monster_angle -= 360;
if (monster_angle < 0)
monster_angle += 360;
spawn_monster(monster_type, v_src, monster_angle, -1);
return;
}
}
v_src = origin + Vector(0, 0, 20); // up a little bit
v_dest = v_src + gpGlobals->v_right * -128; // to the left
UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr);
if (tr.flFraction >= 1.0)
{
v_src = v_dest;
v_dest = v_dest + Vector(0, 0, -200); // down to ground
// try to find the floor...
UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr);
if (tr.flFraction < 1.0) // hit something?
{
monster_angle = view_angle.y - 90.0f; // face the player
if (monster_angle > 360)
monster_angle -= 360;
if (monster_angle < 0)
monster_angle += 360;
spawn_monster(monster_type, v_src, monster_angle, -1);
return;
}
}
v_src = origin + Vector(0, 0, 20); // up a little bit
v_dest = v_src + gpGlobals->v_forward * -128; // to the rear
UTIL_TraceHull(v_src, v_dest, dont_ignore_monsters, 1, pPlayer, &tr);
if (tr.flFraction >= 1.0)
{
v_src = v_dest;
v_dest = v_dest + Vector(0, 0, -200); // down to ground
// try to find the floor...
UTIL_TraceLine(v_src, v_dest, dont_ignore_monsters, pPlayer, &tr);
if (tr.flFraction < 1.0) // hit something?
{
monster_angle = view_angle.y; // face the player
if (monster_angle > 360)
monster_angle -= 360;
if (monster_angle < 0)
monster_angle += 360;
spawn_monster(monster_type, v_src, monster_angle, -1);
return;
}
}
META_CONS("[MONSTER] there's no room to spawn a monster near player \"%s\"!",
player_name);
return;
}
}
}
META_CONS("[MONSTER] usage: monster monster_name player_name | #player_index");
META_CONS("[MONSTER] valid monster_names are:");
msg[0] = 0;
for (index = 0; monster_types[index].name[0]; index++)
{
strcat(msg, monster_types[index].name);
strcat(msg, " ");
if (strlen(msg) > 60)
{
META_CONS("[MONSTER] %s", msg);
msg[0] = 0;
}
}
if (msg[0])
META_CONS("[MONSTER] %s", msg);
}
void mmGameDLLInit( void )
{
// one time initialization stuff here...
RETURN_META(MRES_IGNORED);
}
int mmDispatchSpawn( edict_t *pent )
{
int index;
char *pClassname = (char *)STRING(pent->v.classname);
if (strcmp(pClassname, "worldspawn") == 0)
{
// free any monster class memory not previously freed...
for (index = 0; index < MAX_MONSTER_ENTS; index++)
{
if (monsters[index].pMonster != NULL)
delete monsters[index].pMonster;
}
// do level initialization stuff here...
for (index = 0; monster_types[index].name[0]; index++)
monster_types[index].need_to_precache = FALSE;
world_precache();
monster_spawn_count = 0;
monster_skill_init();
process_monster_precache_cfg();
process_monster_cfg();
check_respawn_time = 0.0;
for (index = 0; index < MAX_MONSTER_ENTS; index++)
{
monsters[index].monster_index = 0;
monsters[index].monster_pent = NULL;
monsters[index].killed = FALSE; // not killed yet
monsters[index].pMonster = NULL;
}
monster_ents_used = 0;
for (index = 0; index < ARRAYSIZE(gDecals); index++ )
gDecals[index].index = DECAL_INDEX( gDecals[index].name );
}
// 0==Success, -1==Failure ?
RETURN_META_VALUE(MRES_IGNORED, 0);
}
void mmDispatchThink( edict_t *pent )
{
for (int index=0; index < monster_ents_used; index++)
{
if (pent == monsters[index].monster_pent)
{
monsters[index].pMonster->Think();
check_monster_dead();
RETURN_META(MRES_SUPERCEDE);
}
}
RETURN_META(MRES_IGNORED);
}
void mmDispatchTouch( edict_t *pentTouched, edict_t *pentOther )
{
for (int index=0; index < monster_ents_used; index++)
{
if ((pentTouched != NULL) &&
(pentTouched == monsters[index].monster_pent))
{
monsters[index].pMonster->Touch(pentOther);
check_monster_dead();
RETURN_META(MRES_SUPERCEDE);
}
}
RETURN_META(MRES_IGNORED);
}
void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax )
{
int index;
CMAGrunt agrunt;
CMApache apache;
CMBarney barney;
CMBigMomma bigmomma;
CMBullsquid bullsquid;
CMController controller;
CMHAssassin hassassin;
CMHeadCrab headcrab;
CMHGrunt hgrunt;
CMHoundeye houndeye;
CMISlave islave;
CMScientist scientist;
CMSqueakGrenade snark;
CMZombie zombie;
g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" );
(g_engfuncs.pfnAddServerCommand)("monster", MonsterCommand);
for (index = 0; monster_types[index].name[0]; index++)
{
if (monster_types[index].need_to_precache)
{
if (dllapi_log->value)
{
LOG_MESSAGE(PLID, "Precaching %s models & sounds...", monster_types[index].name);
}
switch (index)
{
case 0:
agrunt.Precache();
break;
case 1:
apache.Precache();
break;
case 2:
barney.Precache();
break;
case 3:
bigmomma.Precache();
break;
case 4:
bullsquid.Precache();
break;
case 5:
controller.Precache();
break;
case 6:
hassassin.Precache();
break;
case 7:
headcrab.Precache();
break;
case 8:
hgrunt.Precache();
break;
case 9:
houndeye.Precache();
break;
case 10:
islave.Precache();
break;
case 11:
scientist.Precache();
break;
case 12:
snark.Precache();
break;
case 13:
zombie.Precache();
break;
}
}
}
for (index = 0; index < MAX_MONSTER_ENTS; index++)
{
monsters[index].monster_index = 0;
monsters[index].monster_pent = NULL;
monsters[index].killed = FALSE; // not killed yet
monsters[index].pMonster = NULL;
}
monster_ents_used = 0;
RETURN_META(MRES_IGNORED);
}
void mmStartFrame( void )
{
if (check_respawn_time <= gpGlobals->time)
{
check_respawn_time = gpGlobals->time + 1.0;
check_respawn();
}
RETURN_META(MRES_IGNORED);
}
static DLL_FUNCTIONS gFunctionTable =
{
mmGameDLLInit, //! pfnGameInit() Initialize the game (one-time call after loading of game .dll)
mmDispatchSpawn, //! pfnSpawn()
mmDispatchThink, //! pfnThink
NULL, // pfnUse
mmDispatchTouch, //! pfnTouch
NULL, // pfnBlocked
NULL, // pfnKeyValue
NULL, // pfnSave
NULL, // pfnRestore
NULL, // pfnSetAbsBox
NULL, // pfnSaveWriteFields
NULL, // pfnSaveReadFields
NULL, // pfnSaveGlobalState
NULL, // pfnRestoreGlobalState
NULL, // pfnResetGlobalState
NULL, // pfnClientConnect
NULL, // pfnClientDisconnect
NULL, // pfnClientKill
NULL, // pfnClientPutInServer
NULL, // pfnClientCommand
NULL, // pfnClientUserInfoChanged
mmServerActivate, //! pfnServerActivate() (wd) Server is starting a new map
NULL, // pfnServerDeactivate
NULL, // pfnPlayerPreThink
NULL, // pfnPlayerPostThink
mmStartFrame, //! pfnStartFrame
NULL, // pfnParmsNewLevel
NULL, // pfnParmsChangeLevel
NULL, // pfnGetGameDescription
NULL, // pfnPlayerCustomization
NULL, // pfnSpectatorConnect
NULL, // pfnSpectatorDisconnect
NULL, // pfnSpectatorThink
NULL, // pfnSys_Error
NULL, // pfnPM_Move
NULL, // pfnPM_Init
NULL, // pfnPM_FindTextureType
NULL, // pfnSetupVisibility
NULL, // pfnUpdateClientData
NULL, // pfnAddToFullPack
NULL, // pfnCreateBaseline
NULL, // pfnRegisterEncoders
NULL, // pfnGetWeaponData
NULL, // pfnCmdStart
NULL, // pfnCmdEnd
NULL, // pfnConnectionlessPacket
NULL, // pfnGetHullBounds
NULL, // pfnCreateInstancedBaselines
NULL, // pfnInconsistentFile
NULL, // pfnAllowLagCompensation
};
C_DLLEXPORT int GetEntityAPI2( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion )
{
if(!pFunctionTable) {
UTIL_LogPrintf("GetEntityAPI2 called with null pFunctionTable");
return(FALSE);
}
else if(*interfaceVersion != INTERFACE_VERSION) {
UTIL_LogPrintf("GetEntityAPI2 version mismatch; requested=%d ours=%d", *interfaceVersion, INTERFACE_VERSION);
//! Tell engine what version we had, so it can figure out who is out of date.
*interfaceVersion = INTERFACE_VERSION;
return(FALSE);
}
memcpy( pFunctionTable, &gFunctionTable, sizeof( DLL_FUNCTIONS ) );
return(TRUE);
}
void mmDispatchThink_Post( edict_t *pent )
{
check_monster_hurt(pent);
check_monster_dead();
RETURN_META(MRES_IGNORED);
}
void mmPlayerPostThink_Post( edict_t *pEntity )
{
check_monster_hurt(pEntity);
check_monster_dead();
RETURN_META(MRES_IGNORED);
}
static DLL_FUNCTIONS gFunctionTable_Post =
{
NULL, // pfnGameInit() Initialize the game (one-time call after loading of game .dll)
NULL, // pfnSpawn()
mmDispatchThink_Post, //! pfnThink
NULL, // pfnUse
NULL, // pfnTouch
NULL, // pfnBlocked
NULL, // pfnKeyValue
NULL, // pfnSave
NULL, // pfnRestore
NULL, // pfnSetAbsBox
NULL, // pfnSaveWriteFields
NULL, // pfnSaveReadFields
NULL, // pfnSaveGlobalState
NULL, // pfnRestoreGlobalState
NULL, // pfnResetGlobalState
NULL, // pfnClientConnect
NULL, // pfnClientDisconnect
NULL, // pfnClientKill
NULL, // pfnClientPutInServer
NULL, // pfnClientCommand
NULL, // pfnClientUserInfoChanged
NULL, // pfnServerActivate() (wd) Server is starting a new map
NULL, // pfnServerDeactivate
NULL, // pfnPlayerPreThink
mmPlayerPostThink_Post, //! pfnPlayerPostThink
NULL, // pfnStartFrame
NULL, // pfnParmsNewLevel
NULL, // pfnParmsChangeLevel
NULL, // pfnGetGameDescription
NULL, // pfnPlayerCustomization
NULL, // pfnSpectatorConnect
NULL, // pfnSpectatorDisconnect
NULL, // pfnSpectatorThink
NULL, // pfnSys_Error
NULL, // pfnPM_Move
NULL, // pfnPM_Init
NULL, // pfnPM_FindTextureType
NULL, // pfnSetupVisibility
NULL, // pfnUpdateClientData
NULL, // pfnAddToFullPack
NULL, // pfnCreateBaseline
NULL, // pfnRegisterEncoders
NULL, // pfnGetWeaponData
NULL, // pfnCmdStart
NULL, // pfnCmdEnd
NULL, // pfnConnectionlessPacket
NULL, // pfnGetHullBounds
NULL, // pfnCreateInstancedBaselines
NULL, // pfnInconsistentFile
NULL, // pfnAllowLagCompensation
};
C_DLLEXPORT int GetEntityAPI2_Post( DLL_FUNCTIONS *pFunctionTable, int *interfaceVersion )
{
if(!pFunctionTable) {
UTIL_LogPrintf("GetEntityAPI2_Post called with null pFunctionTable");
return(FALSE);
}
else if(*interfaceVersion != INTERFACE_VERSION) {
UTIL_LogPrintf("GetEntityAPI2_Post version mismatch; requested=%d ours=%d", *interfaceVersion, INTERFACE_VERSION);
//! Tell engine what version we had, so it can figure out who is out of date.
*interfaceVersion = INTERFACE_VERSION;
return(FALSE);
}
memcpy( pFunctionTable, &gFunctionTable_Post, sizeof( DLL_FUNCTIONS ) );
return(TRUE);
}