Print number of entities found in BSP file.

Add 'ambient_music' entity.
This commit is contained in:
Giegue
2023-03-25 21:48:27 -03:00
parent c813f6e76a
commit a3086aaaa4
9 changed files with 679 additions and 13 deletions

View File

@@ -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 \

View File

@@ -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

View File

@@ -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;
}
}
}

View File

@@ -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
}

61
src/dlls/music.cpp Normal file
View File

@@ -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();
}

444
src/dlls/ripent.cpp Normal file
View File

@@ -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 <sys/stat.h>
#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;
}

97
src/dlls/ripent.h Normal file
View File

@@ -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);

View File

@@ -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