// // 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); }