diff --git a/src/dlls/Makefile b/src/dlls/Makefile index a87d2d4..60d8010 100644 --- a/src/dlls/Makefile +++ b/src/dlls/Makefile @@ -38,9 +38,11 @@ OBJ = \ monstermaker.o \ monsters.o \ monsterstate.o \ + music.o \ nodes.o \ otis.o \ pitdrone.o \ + ripent.o \ rgrunt.o \ scientist.o \ shock.o \ diff --git a/src/dlls/cmbaseextra.h b/src/dlls/cmbaseextra.h index 69ddd8b..ba38d05 100644 --- a/src/dlls/cmbaseextra.h +++ b/src/dlls/cmbaseextra.h @@ -33,4 +33,18 @@ public: BOOL m_fFadeChildren;// should we make the children fadeout? }; +//========================================================= +// Ambient Music - Plays an mp3 music file to players. +//========================================================= +class CMAmbientMusic : public CMBaseMonster +{ +public: + void Spawn(void); + //void Precache(void); // accessed before entvars are valid, manual precache in monster_config.cpp + void KeyValue(KeyValueData* pkvd); + void EXPORT MusicUse(edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value); + + BOOL m_fPlaying; // music is active +}; + #endif // BASEEXTRA_H diff --git a/src/dlls/dllapi.cpp b/src/dlls/dllapi.cpp index 23678d9..4243c90 100644 --- a/src/dlls/dllapi.cpp +++ b/src/dlls/dllapi.cpp @@ -162,6 +162,7 @@ monster_type_t monster_types[]= "info_node", FALSE, // Nodes "info_node_air", FALSE, "monstermaker", FALSE, // Extra entities + "ambient_music", FALSE, "", FALSE }; @@ -705,6 +706,7 @@ edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawn case 29: monsters[monster_index].pMonster = CreateClassPtr((CMStukabat *)NULL); break; // Extra entities case 32: monsters[monster_index].pMonster = CreateClassPtr((CMMonsterMaker *)NULL); break; + case 33: monsters[monster_index].pMonster = CreateClassPtr((CMAmbientMusic *)NULL); break; } if (monsters[monster_index].pMonster == NULL) @@ -786,7 +788,7 @@ void check_respawn(void) if (spawn_monster(monster_type, origin, angles, spawnflags, keyvalue) == NULL) { // spawn_monster failed - ALERT( at_error, "Failed to spawn %s at origin %f %f %f\n", monster_types[monster_type].name, origin.x, origin.y, origin.z ); + ALERT( at_error, "[MONSTER] Failed to spawn %s at origin %f %f %f\n", monster_types[monster_type].name, origin.x, origin.y, origin.z ); } } } @@ -1416,6 +1418,7 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) // Extra entities CMMonsterMaker monstermaker; // 32 + CMAmbientMusic ambientmusic; g_psv_gravity = CVAR_GET_POINTER( "sv_gravity" ); @@ -1465,6 +1468,7 @@ void mmServerActivate( edict_t *pEdictList, int edictCount, int clientMax ) case 28: rgrunt.Precache(); break; case 29: stukabat.Precache(); break; case 32: monstermaker.Precache(); break; + //case 33: ambientmusic.Precache(); break; } } } diff --git a/src/dlls/monster_config.cpp b/src/dlls/monster_config.cpp index 8ea9ba0..c7ca8da 100644 --- a/src/dlls/monster_config.cpp +++ b/src/dlls/monster_config.cpp @@ -13,6 +13,7 @@ #include "meta_api.h" #include "monster_plugin.h" +#include "ripent.h" extern cvar_t *dllapi_log; @@ -126,6 +127,21 @@ void scan_monster_cfg(FILE *fp) monster = TRUE; } } + else if (strcmp(monster_types[mIndex].name, "ambient_music") == 0) + { + // TODO - Extra entities should go towards a separate counter like nodes + if (monster_spawn_count == MAX_MONSTERS) + { + LOG_MESSAGE(PLID, "ERROR: can't add ambient_music, 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) { // Normal node @@ -260,7 +276,7 @@ void scan_monster_cfg(FILE *fp) { if (monster) { - // only applicable for monstermaket entity + // only applicable for monstermaker entity if (strcmp(data[kvd_index-1].value, "monstermaker") == 0) { // precache the custom model @@ -296,6 +312,22 @@ void scan_monster_cfg(FILE *fp) } } } + else if (strcmp(data[i].key, "message") == 0) + { + if (monster) + { + // only applicable for ambient_music + if (strcmp(data[kvd_index - 1].value, "ambient_music") == 0) + { + // precache the sound here + PRECACHE_GENERIC(data[i].value); + + // the entity will need the keyvalue + 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 { // We do not know this keyvalue, but an specific entity might use it. @@ -374,36 +406,45 @@ void scan_monster_cfg(FILE *fp) bool process_monster_cfg(void) { char game_dir[256]; - char filename[256]; + char BSPfilename[256]; + char CFGfilename[256]; FILE *fp = NULL; monster_spawn_count = 0; + node_spawn_count = 0; // find the directory name of the currently running MOD... (*g_engfuncs.pfnGetGameDir)(game_dir); - strcpy(filename, game_dir); + strcpy(CFGfilename, game_dir); #ifdef __linux__ - strcat(filename, "/maps/"); + strcat(CFGfilename, "/maps/"); #else - strcat(filename, "\\maps\\"); + strcat(CFGfilename, "\\maps\\"); #endif - strcat(filename, STRING(gpGlobals->mapname)); - strcat(filename, "_monster.cfg"); + strcat(CFGfilename, STRING(gpGlobals->mapname)); + strcpy(BSPfilename, CFGfilename); + + strcat(BSPfilename, ".bsp"); + strcat(CFGfilename, "_monster.cfg"); + + LoadBSPFile(BSPfilename); + ParseEntities(); + LOG_MESSAGE(PLID, "It works! LoadBSPFile: Parsed '%i' entities", num_entities); // check if the map specific filename exists... - if (access(filename, 0) == 0) + if (access(CFGfilename, 0) == 0) { if (dllapi_log->value) { //META_CONS("[MONSTER] Processing config file=%s", filename); - LOG_MESSAGE(PLID, "Processing config file=%s", filename); + LOG_MESSAGE(PLID, "Processing config file '%s'", CFGfilename); } - if ((fp = fopen(filename, "r")) == NULL) + if ((fp = fopen(CFGfilename, "r")) == NULL) { //META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename); - LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", filename); + LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", CFGfilename); return TRUE; // error } diff --git a/src/dlls/music.cpp b/src/dlls/music.cpp new file mode 100644 index 0000000..217fb9d --- /dev/null +++ b/src/dlls/music.cpp @@ -0,0 +1,61 @@ +//========================================================= +// Ambient Music - when triggered, it will play an mp3 +// music file locally to players, looped or once. +//========================================================= +#include "extdll.h" +#include "util.h" +#include "cmbase.h" +#include "cmbasemonster.h" +#include "cmbaseextra.h" + +// spawnflags +#define SF_MUSIC_LOOP 2 // music will loop instead of playing once. +#define SF_MUSIC_ACTIVATOR_ONLY 4 // only play to the one that activates this entity. + +void CMAmbientMusic::KeyValue(KeyValueData *pkvd) +{ + CMBaseMonster::KeyValue(pkvd); +} +void CMAmbientMusic::Spawn() +{ + pev->solid = SOLID_NOT; + SetUse(&CMAmbientMusic::MusicUse); + pev->classname = MAKE_STRING("ambient_music"); +} + +void CMAmbientMusic::MusicUse(edict_t *pActivator, edict_t *pCaller, USE_TYPE useType, float value) +{ + // no music + if (FStringNull(pev->message)) + return; + + // not a player (if not to everyone) + if (pev->spawnflags & SF_MUSIC_ACTIVATOR_ONLY && !UTIL_IsPlayer(pActivator)) + return; + + if (pev->spawnflags & SF_MUSIC_ACTIVATOR_ONLY) + MESSAGE_BEGIN(MSG_ALL, SVC_STUFFTEXT); + else + MESSAGE_BEGIN(MSG_ONE, SVC_STUFFTEXT, NULL, pActivator); + + // triggering off + if (useType == USE_OFF || m_fPlaying && useType != USE_ON) + { + WRITE_STRING("mp3 stop\n"); + m_fPlaying = FALSE; + } + else // USE_ON / USE_TOGGLE + { + char szPath[256]; + + if (pev->spawnflags & SF_MUSIC_LOOP) + sprintf(szPath, "mp3 loop %s\n", STRING(pev->message)); + else + sprintf(szPath, "mp3 play %s\n", STRING(pev->message)); + + WRITE_STRING(szPath); + m_fPlaying = TRUE; + } + + MESSAGE_END(); +} diff --git a/src/dlls/ripent.cpp b/src/dlls/ripent.cpp new file mode 100644 index 0000000..583075e --- /dev/null +++ b/src/dlls/ripent.cpp @@ -0,0 +1,444 @@ +/*** +* +* 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. +* +****/ + +// this is a stripped down "bspfile.c" file containing only entity data similar +// to using a ripent tool to read ents from a bsp, hence the name. +// -Giegue +#include "extdll.h" +#include "ripent.h" +#include "meta_api.h" + +#if defined linux +#include +#endif + +//============================================================================= + +int entdatasize; +char dentdata[MAX_MAP_ENTSTRING]; +int dentdata_checksum; + +int num_entities; +entity_t entities[MAX_MAP_ENTITIES]; + +//============================================================================= + +dheader_t *header; + +int CopyLump(int lump, void *dest, int size) +{ + int length, ofs; + + length = header->lumps[lump].filelen; + ofs = header->lumps[lump].fileofs; + + if (length % size) + { + LOG_MESSAGE(PLID, "LoadBSPFile: odd lump size"); + return 0; + } + + memcpy(dest, (byte *)header + ofs, length); + + return length / size; +} + +/* +============= +LoadBSPFile +============= +*/ +void LoadBSPFile(char *filename) +{ + int i; + + // + // load the file header + // + if (LoadFile(filename, (void **)&header) == -1) + return; + + // UNDONE: there is no need to swap it...? -Giegue + // swap the header + /*for (i = 0; i < sizeof(dheader_t) / 4; i++) + ((int *)header)[i] = LittleLong(((int *)header)[i]);*/ + + // game will not load the BSP if it's invalid. + // so if this is called, it means something went really wrong loading it + if (header->version != BSPVERSION) + { + LOG_MESSAGE(PLID, "%s is version %i, not %i", filename, header->version, BSPVERSION); + return; + } + + entdatasize = CopyLump(LUMP_ENTITIES, dentdata, 1); + + free(header); // everything has been copied out +} + +//============================================================================ + +/* +================= +ParseEpair +================= +*/ +epair_t *ParseEpair(void) +{ + epair_t *e; + + e = (epair_t*)malloc(sizeof(epair_t)); + memset(e, 0, sizeof(epair_t)); + + if (strlen(token) >= MAX_KEY - 1) + { + LOG_MESSAGE(PLID, "ParseEpar: token too long [strlen(token) >= MAX_KEY - 1]"); + return NULL; + } + e->key = copystring(token); + GetToken(false); + if (strlen(token) >= MAX_VALUE - 1) + { + LOG_MESSAGE(PLID, "ParseEpar: token too long [strlen(token) >= MAX_VALUE - 1]"); + return NULL; + } + e->value = copystring(token); + + return e; +} + + +/* +================ +ParseEntity +================ +*/ +bool ParseEntity(void) +{ + epair_t *e; + entity_t *mapent; + + if (!GetToken(true)) + return false; + + if (strcmp(token, "{")) + { + LOG_MESSAGE(PLID, "ParseEntity: { not found"); + return false; + } + + if (num_entities == MAX_MAP_ENTITIES) + { + LOG_MESSAGE(PLID, "num_entities == MAX_MAP_ENTITIES"); + return false; + } + + mapent = &entities[num_entities]; + num_entities++; + + do + { + if (!GetToken(true)) + { + LOG_MESSAGE(PLID, "ParseEntity: EOF without closing brace"); + return false; + } + if (!strcmp(token, "}")) + break; + e = ParseEpair(); + e->next = mapent->epairs; + mapent->epairs = e; + } while (1); + + return true; +} + +/* +================ +ParseEntities + +Parses the dentdata string into entities +================ +*/ +void ParseEntities(void) +{ + num_entities = 0; + ParseFromMemory(dentdata, entdatasize); + + while (ParseEntity()) + { + } +} + + +// -- +/* MERGE cmdlib.c AND scriplib.c INTO ripent.cpp */ +/* Only add needed functions. */ +// -- + +// -- cmdlib.c -- +char qdir[1024] = { '\0' }; +int LoadFile(char *filename, void **bufferptr) +{ + FILE *f; + int length; + void *buffer; + + f = SafeOpenRead(filename); + if (f == NULL) + return -1; // error + +#if defined (_WIN32) + length = filelength(fileno(f)); +#else + struct stat st; stat(filename, &st); + length = st.st_size; +#endif + buffer = malloc(length + 1); + ((char *)buffer)[length] = 0; + SafeRead(f, buffer, length); + fclose(f); + + *bufferptr = buffer; + return length; +} +int LittleLong(int l) +{ + byte b1, b2, b3, b4; + + b1 = l & 255; + b2 = (l >> 8) & 255; + b3 = (l >> 16) & 255; + b4 = (l >> 24) & 255; + + return ((int)b1 << 24) + ((int)b2 << 16) + ((int)b3 << 8) + b4; +} +char *copystring(char *s) +{ + char *b; + b = (char*)malloc(strlen(s) + 1); + strcpy(b, s); + return b; +} + +FILE *SafeOpenRead(char *filename) +{ + FILE *f; + + f = fopen(filename, "rb"); + + if (!f) + { + LOG_MESSAGE(PLID, "Error opening %s: %s", filename, strerror(errno)); + return NULL; + } + + return f; +} +void SafeRead(FILE *f, void *buffer, int count) +{ + if (fread(buffer, 1, count, f) != (size_t)count) + { + LOG_MESSAGE(PLID, "File read failure"); + return; + } +} +char *ExpandPath(char *path) +{ + char *psz; + static char full[1024]; + if (!qdir) + { + LOG_MESSAGE(PLID, "ExpandPath called without qdir set"); + return NULL; + } + if (path[0] == '/' || path[0] == '\\' || path[1] == ':') + return path; + psz = strstr(path, qdir); + if (psz) + strcpy(full, path); + else + sprintf(full, "%s%s", qdir, path); + + return full; +} + +// -- scriplib.c -- +typedef struct +{ + char filename[1024]; + char *buffer, *script_p, *end_p; + int line; +} script_t; + +#define MAX_INCLUDES 8 +script_t scriptstack[MAX_INCLUDES]; +script_t *script; +int scriptline; + +char token[MAXTOKEN]; +bool endofscript; +bool tokenready; // only true if UnGetToken was just called + +void ParseFromMemory(char *buffer, int size) +{ + script = scriptstack; + script++; + if (script == &scriptstack[MAX_INCLUDES]) + { + LOG_MESSAGE(PLID, "script file exceeded MAX_INCLUDES"); + return; + } + strcpy(script->filename, "memory buffer"); + + script->buffer = buffer; + script->line = 1; + script->script_p = script->buffer; + script->end_p = script->buffer + size; + + endofscript = false; + tokenready = false; +} +bool EndOfScript(bool crossline) +{ + if (!crossline) + { + LOG_MESSAGE(PLID, "Line %i is incomplete\n", scriptline); + return false; + } + + if (!strcmp(script->filename, "memory buffer")) + { + endofscript = true; + return false; + } + + free(script->buffer); + if (script == scriptstack + 1) + { + endofscript = true; + return false; + } + script--; + scriptline = script->line; + //printf("returning to %s\n", script->filename); + return GetToken(crossline); +} +void AddScriptToStack(char *filename) +{ + int size; + + script++; + if (script == &scriptstack[MAX_INCLUDES]) + { + LOG_MESSAGE(PLID, "script file exceeded MAX_INCLUDES"); + return; + } + strcpy(script->filename, ExpandPath(filename)); + + size = LoadFile(script->filename, (void **)&script->buffer); + + //printf("entering %s\n", script->filename); + + script->line = 1; + + script->script_p = script->buffer; + script->end_p = script->buffer + size; +} +bool GetToken(bool crossline) +{ + char *token_p; + + if (tokenready) // is a token allready waiting? + { + tokenready = false; + return true; + } + + if (script->script_p >= script->end_p) + return EndOfScript(crossline); + + // + // skip space + // +skipspace: + while (*script->script_p <= 32) + { + if (script->script_p >= script->end_p) + return EndOfScript(crossline); + if (*script->script_p++ == '\n') + { + if (!crossline) + { + LOG_MESSAGE(PLID, "Line %i is incomplete\n", scriptline); + return false; + } + scriptline = script->line++; + } + } + + if (script->script_p >= script->end_p) + return EndOfScript(crossline); + + if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field + (*script->script_p == '/' && *((script->script_p) + 1) == '/')) // also make // a comment field + { + if (!crossline) + { + LOG_MESSAGE(PLID, "Line %i is incomplete\n", scriptline); + return false; + } + while (*script->script_p++ != '\n') + if (script->script_p >= script->end_p) + return EndOfScript(crossline); + goto skipspace; + } + + // + // copy token + // + token_p = token; + + if (*script->script_p == '"') + { + // quoted token + script->script_p++; + while (*script->script_p != '"') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + LOG_MESSAGE(PLID, "Token too large on line %i\n", scriptline); + } + script->script_p++; + } + else // regular token + while (*script->script_p > 32 && *script->script_p != ';') + { + *token_p++ = *script->script_p++; + if (script->script_p == script->end_p) + break; + if (token_p == &token[MAXTOKEN]) + LOG_MESSAGE(PLID, "Token too large on line %i\n", scriptline); + } + + *token_p = 0; + + if (!strcmp(token, "$include")) + { + GetToken(false); + AddScriptToStack(token); + return GetToken(crossline); + } + + return true; +} + diff --git a/src/dlls/ripent.h b/src/dlls/ripent.h new file mode 100644 index 0000000..83d7a46 --- /dev/null +++ b/src/dlls/ripent.h @@ -0,0 +1,97 @@ +/*** +* +* 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. +* +****/ + +// this is a stripped down "bspfile.h". contains only entity data similar +// to using a ripent tool to read ents from a bsp, hence the name. +// -Giegue +#include "extdll.h" + +// upper design bounds + +#define MAX_MAP_ENTITIES 1024 +#define MAX_MAP_ENTSTRING (128*1024) + +// key / value pair sizes + +#define MAX_KEY 32 +#define MAX_VALUE 1024 + +//============================================================================= + +#define BSPVERSION 30 + +typedef struct +{ + int fileofs, filelen; +} lump_t; + +#define LUMP_ENTITIES 0 + +#define HEADER_LUMPS 15 + +typedef struct +{ + int version; + lump_t lumps[HEADER_LUMPS]; +} dheader_t; + +//============================================================================ + +// the utilities get to be lazy and just use large static arrays + +extern int entdatasize; +extern char dentdata[MAX_MAP_ENTSTRING]; + +void LoadBSPFile(char *filename); + +//=============== + +typedef struct epair_s +{ + struct epair_s *next; + char *key; + char *value; +} epair_t; + +typedef struct +{ + vec3_t origin; + int firstbrush; + int numbrushes; + epair_t *epairs; +} entity_t; + +extern int num_entities; +extern entity_t entities[MAX_MAP_ENTITIES]; + +void ParseEntities(void); + +epair_t *ParseEpair(void); + +// -- +/* MERGE cmdlib.h AND scriplib.h INTO ripent.h */ +/* Only add needed functions. */ +// -- + +// -- cmdlib.h -- +int LoadFile(char *filename, void **bufferptr); +int LittleLong(int l); +char *copystring(char *s); + +FILE *SafeOpenRead(char *filename); +void SafeRead(FILE *f, void *buffer, int count); +char *ExpandPath(char *path); // from scripts + +// -- scriplib.h -- +#define MAXTOKEN 512 + +extern char token[MAXTOKEN]; +bool GetToken(bool crossline); +void ParseFromMemory(char *buffer, int size); diff --git a/src/dlls/util.h b/src/dlls/util.h index 23506f5..6b52571 100644 --- a/src/dlls/util.h +++ b/src/dlls/util.h @@ -400,6 +400,9 @@ extern DLL_GLOBAL const Vector g_vecZero; #define SVC_ROOMTYPE 37 #define SVC_HLTV 50 +// Added to stuff text to the clients +#define SVC_STUFFTEXT 9 // [string] stuffed into client's console buffer + // prxoy director stuff #define DRC_EVENT 3 // informs the dircetor about ann important game event diff --git a/src/dlls/zombie.cpp b/src/dlls/zombie.cpp index 32b48cc..29c5910 100644 --- a/src/dlls/zombie.cpp +++ b/src/dlls/zombie.cpp @@ -245,7 +245,7 @@ void CMZombie :: HandleAnimEvent( MonsterEvent_t *pEvent ) void CMZombie :: Spawn() { Precache( ); - + SET_MODEL(ENT(pev), (!FStringNull( pev->model ) ? STRING( pev->model ) : "models/zombie.mdl")); UTIL_SetSize( pev, VEC_HUMAN_HULL_MIN, VEC_HUMAN_HULL_MAX );