nuclide/src/server/entityDef.qc

240 lines
6.6 KiB
Plaintext

/*
* Copyright (c) 2023 Vera Visions LLC.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* entityDef implementation
these definitions are a further abstraction from how we view
entity definitions. this system tries to be mostly compatible
with the def system in id Tech 4 (Doom 3, Quake 4, Prey, etc.)
however, we are not aiming for full compatibility right now as
that will require further abstraction.
that said, the origin of this idea dates way back to when
Team Fortress Software created MapC for Team Fortress 2 when
it was originally on Quake II's engine.
http://www.teamfortress.com/tfii/mc2mapc.html (go to the wayback machine for this)
the gist is, that an entity def can set a base spawnclass (e.g. func_door)
and populate it with key/value pairs. the amount of code the programmers
has to implement is massively reduced and we can create prefabs much easier
as a result.
overview:
entityDef func_illusionary {
"spawnclass" "func_wall"
"solid" "0"
"movetype"
"0"
}
*/
/* games can feel free to set this to whatever you need. */
#ifndef ENTITYDEF_MAX
#define ENTITYDEF_MAX 128
#endif
typedef struct
{
string entClass;
string spawnClass;
string spawnData;
string inheritKeys;
} entityDef_t;
entityDef_t g_entDefTable[ENTITYDEF_MAX];
var int g_entDefCount;
void
EntityDef_ReadFile(string filePath)
{
filestream defFile;
string tempString = "";
entityDef_t currentDef;
int braceDepth = 0i;
string lastWord = __NULL__;
currentDef.entClass = "";
currentDef.spawnClass = "";
currentDef.spawnData = "";
currentDef.inheritKeys = "";
/* bounds check */
if (g_entDefCount >= ENTITYDEF_MAX) {
error(sprintf("EntityDef_ReadFile: reached limit of %d defs\n", ENTITYDEF_MAX));
}
/* open file */
defFile = fopen(filePath, FILE_READ);
if (defFile < 0) {
error(sprintf("EntityDef_ReadFile: unable to read %S\n", filePath));
}
/* line by line */
while ((tempString = fgets(defFile))) {
int lineSegments = tokenize_console(tempString);
/* word for word */
for (int i = 0i; i < lineSegments; i++) {
string word = argv(i);
switch (word) {
case "{":
braceDepth++;
break;
case "}":
braceDepth--;
/* we've reached the end of a definition */
if (braceDepth == 0) {
/* we have something somewhat valid I guess */
if (currentDef.entClass != "" && currentDef.spawnClass != "") {
g_entDefTable[g_entDefCount].entClass = currentDef.entClass;
g_entDefTable[g_entDefCount].spawnClass = currentDef.spawnClass;
g_entDefTable[g_entDefCount].spawnData = currentDef.spawnData;
g_entDefTable[g_entDefCount].inheritKeys = currentDef.inheritKeys;
/* increment the def count */
if (g_entDefCount < ENTITYDEF_MAX)
g_entDefCount++;
}
currentDef.entClass = "";
currentDef.spawnClass = "";
currentDef.spawnData = "";
currentDef.inheritKeys = "";
}
break;
default:
/* anything outside braces defines the classname for the next def */
if (braceDepth == 0 && lastWord == "entityDef") {
currentDef.entClass = word;
} else if (braceDepth == 1) {
/* spawnclass is reserved and the next keyword specs it */
if (word == "spawnclass") {
currentDef.spawnClass = argv(i+1);
i++;
} else if (word == "inherit") {
currentDef.inheritKeys = argv(i+1);
i++;
} else if (substring(word, 0, 7) == "editor_") {
/* do nothing */
i++;
} else { /* rest gets dumped into spawndata */
currentDef.spawnData = strcat(currentDef.spawnData, "\"", word, "\"", " ");
}
}
}
lastWord = word;
}
}
/* clean up */
fclose(defFile);
}
void
EntityDef_Init(void)
{
searchhandle pm;
pm = search_begin("def/*.def", TRUE, TRUE);
for (int i = 0; i < search_getsize(pm); i++) {
EntityDef_ReadFile(search_getfilename(pm, i));
}
search_end(pm);
#if 0
for (int i = 0i; i < g_entDefCount; i++) {
int numKeys = tokenize_console(g_entDefTable[i].spawnData);
print(sprintf("edef %i: %S\n", i, g_entDefTable[i].entClass));
print(sprintf("\tspawnclass: %S\n", g_entDefTable[i].spawnClass));
print(sprintf("\tinheritKeys: %S\n", g_entDefTable[i].inheritKeys));
print(sprintf("\tspawnData:\n", g_entDefTable[i].spawnData));
for (int c = 0; c < numKeys; c+=2) {
print(sprintf("\t\t%S %S\n", argv(c), argv(c+1)));
}
}
#endif
}
static void
EntityDef_PrepareEntity(entity target, int id)
{
string spawnClass;
int spawnWords = 0i;
NSEntity targetEnt = (NSEntity)target;
entity oldSelf = self;
/* first we spawn it as the base spawnclass */
if (!isfunction(g_entDefTable[id].spawnClass)) {
spawnClass = strcat("spawnfunc_", g_entDefTable[id].spawnClass);
} else {
spawnClass = g_entDefTable[id].spawnClass;
}
/* init */
self = target;
callfunction(spawnClass);
self = oldSelf;
/* first load all keys we inherit from the 'inherited' class */
for (int x = 0; x < g_entDefCount; x++) {
/* found the thing we're supposed to inherit */
if (g_entDefTable[x].entClass == g_entDefTable[id].inheritKeys) {
spawnWords = tokenize_console(g_entDefTable[x].spawnData);
for (int i = 0; i < spawnWords; i+= 2) {
targetEnt.SpawnKey(argv(i), argv(i+1));
}
}
}
/* now we load the field overrides from the entDef */
spawnWords = tokenize_console(g_entDefTable[id].spawnData);
for (int i = 0; i < spawnWords; i+= 2) {
targetEnt.SpawnKey(argv(i), argv(i+1));
}
/* now we load our own spawndata, which starts and ends with braces */
spawnWords = tokenize_console(__fullspawndata);
for (int i = 1; i < (spawnWords - 1); i+= 2) {
/* ignore this, always */
if (argv(i) != "classname")
targetEnt.SpawnKey(argv(i), argv(i+1));
}
targetEnt.Spawned();
targetEnt.Respawn();
/* now we rename the classname for better visibility */
self.classname = g_entDefTable[id].entClass;
__fullspawndata = "";
}
bool
EntityDef_SpawnClassname(string className)
{
for (int i = 0i; i < g_entDefCount; i++) {
if (className == g_entDefTable[i].entClass) {
EntityDef_PrepareEntity(self, i);
return true;
}
}
return false;
}