Read entities within the BSP itself.

NOT YET COMPLETE, HIGHLY UNSTABLE!

Disabled by default, prone to crashing under Windows.
Set CVar "monster_entity_config" to 0 (or 2) to enable reading from BSP.
This commit is contained in:
Giegue
2023-03-29 02:48:18 -03:00
parent 074635bf8d
commit 79d4b3b21d
5 changed files with 363 additions and 27 deletions

View File

@@ -165,8 +165,6 @@ There are probably more issues that aren't listed here, I'll attempt to fix them
Attempting to recreate everything in one go is a daunting task.
Let it be known that the original 2002 source code will NOT compile on today's compilers, and does NOT contain all the necessary files for compilation. The preliminary was to rewrite and provide as many files or lines of code to ensure it can compile again, and be usable on an actual HLDS installation.
The original Visual C++ 6.0 DSP file exists in the repository but neither the file nor the code has been updated to newer formats. Don't expect it to compile on modern Visual Studio versions.
Current milestones are separated by "Tiers", which are as follows:
### Tier 0
@@ -206,6 +204,7 @@ Current milestones are separated by "Tiers", which are as follows:
- Add configurations to change AI behaviour.
- Optimize code and enhance the AI.
- Create "tool" entities for easier map customization.
- Do more fixes not covered in Tier 4.
What will the future hold after all Tiers has been completed?

View File

@@ -724,6 +724,9 @@ edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawn
monster_pent->v.origin = origin;
monster_pent->v.angles = angles;
// Pass spawnflags first if no keyvalue data exists for it
monster_pent->v.spawnflags = spawnflags;
// Keyvalue data
if (keyvalue != NULL)
{
@@ -738,8 +741,6 @@ edict_t* spawn_monster(int monster_type, Vector origin, Vector angles, int spawn
}
}
monster_pent->v.spawnflags = spawnflags;
monsters[monster_index].pMonster->Spawn();
// Only modify starting spawnflags for monsters, not for entities!

View File

@@ -89,6 +89,8 @@ cvar_t init_monster_show_info = {"monster_show_info", "1", FCVAR_EXTDLL, 0, NULL
cvar_t *monster_show_info = NULL;
cvar_t init_monster_turn_coeficient = {"monster_turn_coeficient", "1.75", FCVAR_EXTDLL, 0, NULL};
cvar_t *monster_turn_coeficient = NULL;
cvar_t init_monster_entity_config = {"monster_entity_config", "1", FCVAR_EXTDLL, 0, NULL};
cvar_t *monster_entity_config = NULL;
// Metamod requesting info about this plugin:
@@ -151,6 +153,9 @@ C_DLLEXPORT int Meta_Attach(PLUG_LOADTIME now, META_FUNCTIONS *pFunctionTable, m
CVAR_REGISTER(&init_monster_turn_coeficient);
monster_turn_coeficient = CVAR_GET_POINTER("monster_turn_coeficient");
CVAR_REGISTER(&init_monster_entity_config);
monster_entity_config = CVAR_GET_POINTER("monster_entity_config");
return(TRUE);
}

View File

@@ -16,6 +16,7 @@
#include "ripent.h"
extern cvar_t *dllapi_log;
extern cvar_t *monster_entity_config;
extern monster_type_t monster_types[];
extern int monster_spawn_count;
@@ -403,6 +404,314 @@ void scan_monster_cfg(FILE *fp)
}
}
void scan_monster_bsp(void)
{
// TODO: code duplication galore! optimize this for T5 milestone. -Giegue
epair_t *kv_pair;
pKVD data[MAX_KEYVALUES];
int kvd_index;
int duplicate_ent;
bool use_monstermod;
int classname_kvdI, mIndex;
float x, y, z;
bool badent, monster, node;
// go through all entities
for (int ent = 1; ent < num_entities; ent++)
{
kv_pair = entities[ent].epairs;
kvd_index = 0;
duplicate_ent = 0;
use_monstermod = true;
classname_kvdI = 0;
badent = monster = node = false;
// examine all keys
while (kv_pair != NULL)
{
if (strcmp(kv_pair->key, "classname") == 0)
{
// the entity we are trying to spawn could already exist within the game
// use the engine's CREATE_NAMED_ENTITY to see if it's valid or not
//
// if it is valid, this entity already exists and we should ignore it
edict_t *existsGAME = CREATE_NAMED_ENTITY( MAKE_STRING( kv_pair->value ) );
if ( !FNullEnt( existsGAME ) )
{
for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++)
{
if (strcmp(kv_pair->value, monster_types[mIndex].name) == 0)
{
// the entity exists BOTH in the game and monstermod!
// keep track of it
duplicate_ent = ent;
break;
}
}
UTIL_Remove( existsGAME ); // get rid of the temporary entity
use_monstermod = false; // use game entity
}
}
else if (duplicate_ent && strcmp(kv_pair->key, "use_monstermod") == 0)
{
if (atoi(kv_pair->value) == 1)
{
// EXPLICITY requested to use the monstermod entity
use_monstermod = true;
}
}
strcpy(data[kvd_index].key, kv_pair->key);
strcpy(data[kvd_index].value, kv_pair->value);
kvd_index++;
kv_pair = kv_pair->next;
}
// spawn a monstermod entity?
if (use_monstermod)
{
// find classname keyvalue
for (int i = 0; i < kvd_index; i++)
{
if (strcmp(data[i].key, "classname") == 0)
{
for (mIndex = 0; monster_types[mIndex].name[0]; mIndex++)
{
if (strcmp(data[i].value, monster_types[mIndex].name) == 0)
{
// Match found, check if it's a node
if (strcmp(monster_types[mIndex].name, "info_node") == 0)
{
// Normal node
if (node_spawn_count == MAX_NODES)
{
LOG_MESSAGE(PLID, "ERROR: can't add node, reached MAX_NODES!");
badent = true;
}
else
node = true;
}
else if (strcmp(monster_types[mIndex].name, "info_node_air") == 0)
{
// Aerial node
if (node_spawn_count == MAX_NODES)
{
LOG_MESSAGE(PLID, "ERROR: can't add node, reached MAX_NODES!");
badent = true;
}
else
{
node_spawnpoint[node_spawn_count].is_air_node = true;
node = true;
}
}
else
{
// Assume it's a monster and add it to the list
// (Extra entities are built as CMBaseMonster)
if (monster_spawn_count == MAX_MONSTERS)
{
LOG_MESSAGE(PLID, "ERROR: can't add entity, reached MAX_MONSTERS!");
badent = true;
}
else
{
monster_spawnpoint[monster_spawn_count].monster = mIndex;
monster_types[mIndex].need_to_precache = true;
monster = true;
}
}
classname_kvdI = i;
break;
}
}
if (monster_types[mIndex].name[0] == 0)
{
LOG_MESSAGE(PLID, "unknown classname: %s", data[i].value);
badent = true;
}
}
}
if (!badent)
{
// Make room for entity-specific keyvalues.
if (monster)
{
// Can I use malloc/calloc again or you are going to crash cuz you feel like it? >.>
monster_spawnpoint[monster_spawn_count].keyvalue = (pKVD*)calloc(MAX_KEYVALUES, sizeof(*monster_spawnpoint[monster_spawn_count].keyvalue));
}
// process entity keyvalues
for (int i = 0; i < kvd_index; i++)
{
// duplicates are overwritten
if (strcmp(data[i].key, "origin") == 0)
{
if (sscanf(data[i].value, "%f %f %f", &x, &y, &z) != 3)
{
LOG_MESSAGE(PLID, "ERROR: invalid origin: %s", data[i].value); // print conflictive line
// reset origin to g_vecZero
LOG_MESSAGE(PLID, "ERROR: entity will spawn at 0 0 0");
x = y = z = 0;
}
if (monster)
{
monster_spawnpoint[monster_spawn_count].origin[0] = x;
monster_spawnpoint[monster_spawn_count].origin[1] = y;
monster_spawnpoint[monster_spawn_count].origin[2] = z;
}
else if (node)
{
node_spawnpoint[node_spawn_count].origin[0] = x;
node_spawnpoint[node_spawn_count].origin[1] = y;
node_spawnpoint[node_spawn_count].origin[2] = z;
}
}
else if (strcmp(data[i].key, "angles") == 0)
{
if (monster)
{
if (sscanf(data[i].value, "%f %f %f", &x, &y, &z) != 3)
{
LOG_MESSAGE(PLID, "ERROR: invalid angles: %s", data[i].value); // print conflictive line
// reset angles to g_vecZero
LOG_MESSAGE(PLID, "ERROR: entity angles will be set to 0 0 0");
x = y = z = 0;
}
monster_spawnpoint[monster_spawn_count].angles[0] = x;
monster_spawnpoint[monster_spawn_count].angles[1] = y;
monster_spawnpoint[monster_spawn_count].angles[2] = z;
}
}
else if (strcmp(data[i].key, "spawnflags") == 0)
{
if (monster)
{
if (sscanf(data[i].value, "%f", &x) != 1)
{
LOG_MESSAGE(PLID, "ERROR: invalid spawnflags: %s", data[i].value); // print conflictive line
// default to no spawnflags
LOG_MESSAGE(PLID, "ERROR: entity spawnflags will be set to none (0)");
x = 0;
}
monster_spawnpoint[monster_spawn_count].spawnflags = x;
}
}
else if (strcmp(data[i].key, "model") == 0)
{
if (monster)
{
// only applicable for normal monsters
if (strcmp(data[classname_kvdI].value, "monstermaker") != 0)
{
// precache the custom model here
PRECACHE_MODEL( 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 if (strcmp(data[i].key, "new_model") == 0)
{
if (monster)
{
// only applicable for monstermaker entity
if (strcmp(data[classname_kvdI].value, "monstermaker") == 0)
{
// precache the custom model
PRECACHE_MODEL( data[i].value );
// the entity will need the keyvalue as well
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 if (strcmp(data[i].key, "monstertype") == 0)
{
if (monster)
{
// this keyvalue is only valid for monstermaker entity
if (strcmp(data[classname_kvdI].value, "monstermaker") == 0)
{
// process the entity precache here
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 if (strcmp(data[i].key, "message") == 0)
{
if (monster)
{
// only applicable for ambient_music
if (strcmp(data[classname_kvdI].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.
// Save it for later
if (monster)
{
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].key, data[i].key);
strcpy(monster_spawnpoint[monster_spawn_count].keyvalue[i].value, data[i].value);
}
}
}
if (monster)
{
// Spawn right away
monster_spawnpoint[monster_spawn_count].need_to_respawn = TRUE;
monster_spawn_count++;
}
else if (node)
{
// Increase node count
node_spawn_count++;
}
// Log on? Print all the entities that were added
if (dllapi_log->value)
{
// Classname only, or we will flood the server!
LOG_CONSOLE(PLID, "[DEBUG] Added entity: %s", data[classname_kvdI].value);
}
}
}
}
}
bool process_monster_cfg(void)
{
char game_dir[256];
@@ -427,32 +736,49 @@ bool process_monster_cfg(void)
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(CFGfilename, 0) == 0)
// process config files?
// -1 = don't process monster config, dynamic spawns only
// 0 = read entities from BSP file
// 1 = read entities from CFG file
// 2 = read entities from both, BSP first, then CFG file
if (monster_entity_config->value >= 0)
{
if (dllapi_log->value)
// read from bsp? (mode 0 or 2)
if (monster_entity_config->value != 1)
{
//META_CONS("[MONSTER] Processing config file=%s", filename);
LOG_MESSAGE(PLID, "Processing config file '%s'", CFGfilename);
LoadBSPFile(BSPfilename);
ParseEntities();
scan_monster_bsp();
}
if ((fp = fopen(CFGfilename, "r")) == NULL)
// read from cfg? (mode 1 or 2)
if (monster_entity_config->value > 0)
{
//META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename);
LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", CFGfilename);
return TRUE; // error
// check if the map specific filename exists...
if (access(CFGfilename, 0) == 0)
{
if (dllapi_log->value)
{
//META_CONS("[MONSTER] Processing config file=%s", filename);
LOG_MESSAGE(PLID, "Processing config file '%s'", CFGfilename);
}
if ((fp = fopen(CFGfilename, "r")) == NULL)
{
//META_CONS("[MONSTER] ERROR: Could not open \"%s\"!", filename);
LOG_MESSAGE(PLID, "ERROR: Could not open \"%s\" file!", CFGfilename);
return TRUE; // error
}
scan_monster_cfg(fp);
fclose(fp);
}
}
scan_monster_cfg(fp);
fclose(fp);
}
return FALSE; // all ok
}

View File

@@ -23,7 +23,6 @@
int entdatasize;
char dentdata[MAX_MAP_ENTSTRING];
int dentdata_checksum;
int num_entities;
entity_t entities[MAX_MAP_ENTITIES];
@@ -57,8 +56,14 @@ LoadBSPFile
*/
void LoadBSPFile(char *filename)
{
int i;
//int i;
// reset these values
entdatasize = 0;
num_entities = 0;
memset(dentdata, 0, sizeof(dentdata));
memset(entities, 0, sizeof(entities));
//
// load the file header
//