906 lines
18 KiB
C
906 lines
18 KiB
C
/*
|
|
Copyright (C) 1996-2001 Id Software, Inc.
|
|
Copyright (C) 2002-2009 John Fitzgibbons and others
|
|
Copyright (C) 2010-2014 QuakeSpasm developers
|
|
|
|
This program 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 program 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 program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
// sv_edict.c -- entity dictionary
|
|
|
|
#include "q_defs.h"
|
|
|
|
dprograms_t progs;
|
|
dfunction_t *pr_functions;
|
|
dstatement_t *pr_statements;
|
|
byte *pr_global_data;
|
|
|
|
ddef_t *pr_fielddefs;
|
|
int32_t pr_edict_size; // in bytes
|
|
|
|
bool pr_alpha_supported; //johnfitz
|
|
|
|
uint16_t pr_crc;
|
|
|
|
static int32_t type_size[] =
|
|
{
|
|
[ev_void] = 1,
|
|
[ev_string] = 1,
|
|
[ev_float] = 1,
|
|
[ev_vector] = 3,
|
|
[ev_entity] = 1,
|
|
[ev_field] = 1,
|
|
[ev_function] = 1,
|
|
[ev_pointer] = 1,
|
|
};
|
|
|
|
#define MAX_FIELD_LEN 64
|
|
#define GEFV_CACHESIZE 2
|
|
|
|
typedef struct
|
|
{
|
|
ddef_t *pcache;
|
|
char field[MAX_FIELD_LEN];
|
|
} gefv_cache;
|
|
|
|
static gefv_cache gefvCache[GEFV_CACHESIZE] =
|
|
{
|
|
{ NULL, "" },
|
|
{ NULL, "" }
|
|
};
|
|
|
|
/*
|
|
=================
|
|
ED_ClearEdict
|
|
|
|
Sets everything to NULL
|
|
=================
|
|
*/
|
|
void ED_ClearEdict(edict_t *e)
|
|
{
|
|
memset(e->fields, 0, progs.entityfields * 4);
|
|
e->free = false;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
ED_Alloc
|
|
|
|
Either finds a free edict, or allocates a new one.
|
|
Try to avoid reusing an entity that was recently freed, because it
|
|
can cause the client to think the entity morphed into something else
|
|
instead of being removed and recreated, which can cause interpolated
|
|
angles and bad trails.
|
|
=================
|
|
*/
|
|
edict_t *ED_Alloc(void)
|
|
{
|
|
int32_t i;
|
|
edict_t *e;
|
|
|
|
for(i = svs.maxclients + 1; i < sv.num_edicts; i++)
|
|
{
|
|
e = EDICT_NUM(i);
|
|
// the first couple seconds of server time can involve a lot of
|
|
// freeing and allocating, so relax the replacement policy
|
|
if(e->free && (e->freetime < 2 || sv.time - e->freetime > 0.5))
|
|
{
|
|
ED_ClearEdict(e);
|
|
return e;
|
|
}
|
|
}
|
|
|
|
if(i == sv.max_edicts) //johnfitz -- use sv.max_edicts instead of MAX_EDICTS
|
|
Host_Error("ED_Alloc: no free edicts (max_edicts is %" PRIi32 ")", sv.max_edicts);
|
|
|
|
sv.num_edicts++;
|
|
e = EDICT_NUM(i);
|
|
memset(e, 0, pr_edict_size); // ericw -- switched sv.edicts to malloc(), so we are accessing uninitialized memory and must fully zero it, not just ED_ClearEdict
|
|
|
|
return e;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
ED_Free
|
|
|
|
Marks the edict as free
|
|
FIXME: walk all entities and NULL out references to this entity
|
|
=================
|
|
*/
|
|
void ED_Free(edict_t *ed)
|
|
{
|
|
SV_UnlinkEdict(ed); // unlink from world bsp
|
|
|
|
ed->free = true;
|
|
ED_RSTRING(ed, ED_model) = 0;
|
|
ED_FLOAT(ed, ED_takedamage) = 0;
|
|
ED_FLOAT(ed, ED_modelindex) = 0;
|
|
ED_FLOAT(ed, ED_colormap) = 0;
|
|
ED_FLOAT(ed, ED_skin) = 0;
|
|
ED_FLOAT(ed, ED_frame) = 0;
|
|
VectorCopy(vec3_origin, ED_VECTOR(ed, ED_origin));
|
|
VectorCopy(vec3_origin, ED_VECTOR(ed, ED_angles));
|
|
ED_FLOAT(ed, ED_nextthink) = -1;
|
|
ED_FLOAT(ed, ED_solid) = 0;
|
|
ed->alpha = ENTALPHA_DEFAULT; //johnfitz -- reset alpha for next entity
|
|
|
|
ed->freetime = sv.time;
|
|
}
|
|
|
|
//===========================================================================
|
|
|
|
/*
|
|
============
|
|
ED_GlobalAtOfs
|
|
============
|
|
*/
|
|
ddef_t *ED_GlobalAtOfs(int32_t ofs)
|
|
{
|
|
ddef_t *def;
|
|
int32_t i;
|
|
|
|
for(i = 0; i < progs.numglobaldefs; i++)
|
|
{
|
|
def = &pr_globaldefs[i];
|
|
if(def->ofs == ofs)
|
|
return def;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
============
|
|
ED_FieldAtOfs
|
|
============
|
|
*/
|
|
ddef_t *ED_FieldAtOfs(int32_t ofs)
|
|
{
|
|
ddef_t *def;
|
|
int32_t i;
|
|
|
|
for(i = 0; i < progs.numfielddefs; i++)
|
|
{
|
|
def = &pr_fielddefs[i];
|
|
if(def->ofs == ofs)
|
|
return def;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
============
|
|
ED_FindField
|
|
============
|
|
*/
|
|
static ddef_t *ED_FindField(const char *name)
|
|
{
|
|
ddef_t *def;
|
|
int32_t i;
|
|
|
|
for(i = 0; i < progs.numfielddefs; i++)
|
|
{
|
|
def = &pr_fielddefs[i];
|
|
if(!strcmp(PR_GetString(def->s_name), name))
|
|
return def;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
ED_FindGlobal
|
|
============
|
|
*/
|
|
static ddef_t *ED_FindGlobal(const char *name)
|
|
{
|
|
ddef_t *def;
|
|
int32_t i;
|
|
|
|
for(i = 0; i < progs.numglobaldefs; i++)
|
|
{
|
|
def = &pr_globaldefs[i];
|
|
if(!strcmp(PR_GetString(def->s_name), name))
|
|
return def;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
ED_FindFunction
|
|
============
|
|
*/
|
|
static dfunction_t *ED_FindFunction(const char *fn_name)
|
|
{
|
|
dfunction_t *func;
|
|
int32_t i;
|
|
|
|
for(i = 0; i < progs.numfunctions; i++)
|
|
{
|
|
func = &pr_functions[i];
|
|
if(!strcmp(PR_GetString(func->s_name), fn_name))
|
|
return func;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
============
|
|
GetEdictFieldValue
|
|
============
|
|
*/
|
|
eval_t *GetEdictFieldValue(edict_t *ed, const char *field)
|
|
{
|
|
ddef_t *def = NULL;
|
|
int32_t i;
|
|
static int32_t rep = 0;
|
|
|
|
for(i = 0; i < GEFV_CACHESIZE; i++)
|
|
{
|
|
if(!strcmp(field, gefvCache[i].field))
|
|
{
|
|
def = gefvCache[i].pcache;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
def = ED_FindField(field);
|
|
|
|
if(strlen(field) < MAX_FIELD_LEN)
|
|
{
|
|
gefvCache[rep].pcache = def;
|
|
strcpy(gefvCache[rep].field, field);
|
|
rep ^= 1;
|
|
}
|
|
|
|
done:
|
|
if(!def)
|
|
return NULL;
|
|
|
|
return ED_EVAL(ed, def->ofs);
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
ED_Print
|
|
|
|
For debugging
|
|
=============
|
|
*/
|
|
void ED_Print(edict_t *ed)
|
|
{
|
|
ddef_t *d;
|
|
int32_t *v;
|
|
int32_t i, j, l;
|
|
const char *name;
|
|
int32_t type;
|
|
|
|
if(ed->free)
|
|
{
|
|
Con_Printf("FREE\n");
|
|
return;
|
|
}
|
|
|
|
Con_SafePrintf("\nEDICT %" PRIi32 ":\n", NUM_FOR_EDICT(ed)); //johnfitz -- was Con_Printf
|
|
for(i = 1; i < progs.numfielddefs; i++)
|
|
{
|
|
d = &pr_fielddefs[i];
|
|
name = PR_GetString(d->s_name);
|
|
l = strlen(name);
|
|
if(l > 1 && name[l - 2] == '_')
|
|
continue; // skip _x, _y, _z vars
|
|
|
|
v = &ED_INT(ed, d->ofs);
|
|
|
|
// if the value is still all 0, skip the field
|
|
type = d->type & ~DEF_SAVEGLOBAL;
|
|
for(j = 0; j < type_size[type]; j++)
|
|
{
|
|
if(v[j])
|
|
break;
|
|
}
|
|
if(j == type_size[type])
|
|
continue;
|
|
|
|
Con_SafePrintf("%s", name); //johnfitz -- was Con_Printf
|
|
while(l++ < 15)
|
|
Con_SafePrintf(" "); //johnfitz -- was Con_Printf
|
|
|
|
Con_SafePrintf("%s\n", PR_ValueString(d->type, (eval_t *)v)); //johnfitz -- was Con_Printf
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
ED_Write
|
|
|
|
For savegames
|
|
=============
|
|
*/
|
|
void ED_Write(FILE *f, edict_t *ed)
|
|
{
|
|
ddef_t *d;
|
|
int32_t *v;
|
|
int32_t i, j;
|
|
const char *name;
|
|
int32_t type;
|
|
|
|
fprintf(f, "{\n");
|
|
|
|
if(ed->free)
|
|
{
|
|
fprintf(f, "}\n");
|
|
return;
|
|
}
|
|
|
|
for(i = 1; i < progs.numfielddefs; i++)
|
|
{
|
|
d = &pr_fielddefs[i];
|
|
name = PR_GetString(d->s_name);
|
|
j = strlen(name);
|
|
if(j > 1 && name[j - 2] == '_')
|
|
continue; // skip _x, _y, _z vars
|
|
|
|
v = &ED_INT(ed, d->ofs);
|
|
|
|
// if the value is still all 0, skip the field
|
|
type = d->type & ~DEF_SAVEGLOBAL;
|
|
for(j = 0; j < type_size[type]; j++)
|
|
{
|
|
if(v[j])
|
|
break;
|
|
}
|
|
if(j == type_size[type])
|
|
continue;
|
|
|
|
fprintf(f, "\"%s\" ", name);
|
|
fprintf(f, "\"%s\"\n", PR_UglyValueString(d->type, (eval_t *)v));
|
|
}
|
|
|
|
//johnfitz -- save entity alpha manually when program doesn't know about alpha
|
|
if(!pr_alpha_supported && ed->alpha != ENTALPHA_DEFAULT)
|
|
fprintf(f, "\"alpha\" \"%f\"\n", ENTALPHA_TOSAVE(ed->alpha));
|
|
//johnfitz
|
|
|
|
fprintf(f, "}\n");
|
|
}
|
|
|
|
void ED_PrintNum(int32_t ent)
|
|
{
|
|
ED_Print(EDICT_NUM(ent));
|
|
}
|
|
|
|
/*
|
|
=============
|
|
ED_PrintEdicts
|
|
|
|
For debugging, prints all the entities in the current server
|
|
=============
|
|
*/
|
|
void ED_PrintEdicts(void)
|
|
{
|
|
int32_t i;
|
|
|
|
if(!sv.active)
|
|
return;
|
|
|
|
Con_Printf("%" PRIi32 " entities\n", sv.num_edicts);
|
|
for(i = 0; i < sv.num_edicts; i++)
|
|
ED_PrintNum(i);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
ED_PrintEdict_f
|
|
|
|
For debugging, prints a single edicy
|
|
=============
|
|
*/
|
|
static void ED_PrintEdict_f(void)
|
|
{
|
|
int32_t i;
|
|
|
|
if(!sv.active)
|
|
return;
|
|
|
|
i = atoi(Cmd_Argv(1));
|
|
if(i < 0 || i >= sv.num_edicts)
|
|
{
|
|
Con_Printf("Bad edict number\n");
|
|
return;
|
|
}
|
|
ED_PrintNum(i);
|
|
}
|
|
|
|
/*
|
|
=============
|
|
ED_Count
|
|
|
|
For debugging
|
|
=============
|
|
*/
|
|
static void ED_Count(void)
|
|
{
|
|
edict_t *ent;
|
|
int32_t i, active, models, solid, step;
|
|
|
|
if(!sv.active)
|
|
return;
|
|
|
|
active = models = solid = step = 0;
|
|
for(i = 0; i < sv.num_edicts; i++)
|
|
{
|
|
ent = EDICT_NUM(i);
|
|
if(ent->free)
|
|
continue;
|
|
active++;
|
|
if(ED_FLOAT(ent, ED_solid))
|
|
solid++;
|
|
if(ED_RSTRING(ent, ED_model))
|
|
models++;
|
|
if(ED_FLOAT(ent, ED_movetype) == MOVETYPE_STEP)
|
|
step++;
|
|
}
|
|
|
|
Con_Printf("num_edicts:%3" PRIi32 "\n", sv.num_edicts);
|
|
Con_Printf("active :%3" PRIi32 "\n", active);
|
|
Con_Printf("view :%3" PRIi32 "\n", models);
|
|
Con_Printf("touch :%3" PRIi32 "\n", solid);
|
|
Con_Printf("step :%3" PRIi32 "\n", step);
|
|
}
|
|
|
|
|
|
/*
|
|
==============================================================================
|
|
|
|
ARCHIVING GLOBALS
|
|
|
|
FIXME: need to tag constants, doesn't really work
|
|
==============================================================================
|
|
*/
|
|
|
|
/*
|
|
=============
|
|
ED_WriteGlobals
|
|
=============
|
|
*/
|
|
void ED_WriteGlobals(FILE *f)
|
|
{
|
|
ddef_t *def;
|
|
int32_t i;
|
|
const char *name;
|
|
int32_t type;
|
|
|
|
fprintf(f, "{\n");
|
|
for(i = 0; i < progs.numglobaldefs; i++)
|
|
{
|
|
def = &pr_globaldefs[i];
|
|
type = def->type;
|
|
if(!(def->type & DEF_SAVEGLOBAL))
|
|
continue;
|
|
type &= ~DEF_SAVEGLOBAL;
|
|
|
|
if(type != ev_string && type != ev_float && type != ev_entity)
|
|
continue;
|
|
|
|
name = PR_GetString(def->s_name);
|
|
fprintf(f, "\"%s\" ", name);
|
|
fprintf(f, "\"%s\"\n", PR_UglyValueString(type, G_EVAL(def->ofs)));
|
|
}
|
|
fprintf(f, "}\n");
|
|
}
|
|
|
|
/*
|
|
=============
|
|
ED_ParseGlobals
|
|
=============
|
|
*/
|
|
const char *ED_ParseGlobals(const char *data)
|
|
{
|
|
char keyname[64];
|
|
ddef_t *key;
|
|
|
|
while(1)
|
|
{
|
|
// parse key
|
|
data = COM_Parse(data);
|
|
if(com_token[0] == '}')
|
|
break;
|
|
if(!data)
|
|
Host_Error("ED_ParseEntity: EOF without closing brace");
|
|
|
|
q_strlcpy(keyname, com_token, sizeof(keyname));
|
|
|
|
// parse value
|
|
data = COM_Parse(data);
|
|
if(!data)
|
|
Host_Error("ED_ParseEntity: EOF without closing brace");
|
|
|
|
if(com_token[0] == '}')
|
|
Host_Error("ED_ParseEntity: closing brace without data");
|
|
|
|
key = ED_FindGlobal(keyname);
|
|
if(!key)
|
|
{
|
|
Con_Printf("'%s' is not a global\n", keyname);
|
|
continue;
|
|
}
|
|
|
|
if(!ED_ParseEpair(pr_global_data, key, com_token))
|
|
Host_Error("ED_ParseGlobals: parse error");
|
|
}
|
|
return data;
|
|
}
|
|
|
|
//============================================================================
|
|
|
|
|
|
/*
|
|
=============
|
|
ED_NewString
|
|
=============
|
|
*/
|
|
static string_t ED_NewString(const char *string)
|
|
{
|
|
char *new_p;
|
|
int32_t i, l;
|
|
string_t num;
|
|
|
|
l = strlen(string) + 1;
|
|
num = PR_AllocString(l, &new_p);
|
|
|
|
for(i = 0; i < l; i++)
|
|
{
|
|
if(string[i] == '\\' && i < l - 1)
|
|
{
|
|
i++;
|
|
if(string[i] == 'n')
|
|
*new_p++ = '\n';
|
|
else
|
|
*new_p++ = '\\';
|
|
}
|
|
else
|
|
*new_p++ = string[i];
|
|
}
|
|
|
|
return num;
|
|
}
|
|
|
|
|
|
/*
|
|
=============
|
|
ED_ParseEval
|
|
=============
|
|
*/
|
|
bool ED_ParseEpair(void *base, ddef_t *key, const char *s)
|
|
{
|
|
int32_t i;
|
|
char string[128];
|
|
ddef_t *def;
|
|
char *v, *w;
|
|
char *end;
|
|
void *d;
|
|
dfunction_t *func;
|
|
|
|
d = (int32_t *)base + key->ofs;
|
|
|
|
switch(key->type & ~DEF_SAVEGLOBAL)
|
|
{
|
|
case ev_string:
|
|
*(string_t *)d = ED_NewString(s);
|
|
break;
|
|
|
|
case ev_float:
|
|
*(float *)d = atof(s);
|
|
break;
|
|
|
|
case ev_vector:
|
|
q_strlcpy(string, s, sizeof(string));
|
|
end = (char *)string + strlen(string);
|
|
v = string;
|
|
w = string;
|
|
|
|
for(i = 0; i < 3 && (w <= end); i++) // ericw -- added (w <= end) check
|
|
{
|
|
// set v to the next space (or 0 byte), and change that char to a 0 byte
|
|
while(*v && *v != ' ')
|
|
v++;
|
|
*v = 0;
|
|
((float *)d)[i] = atof(w);
|
|
w = v = v + 1;
|
|
}
|
|
// ericw -- fill remaining elements to 0 in case we hit the end of string
|
|
// before reading 3 floats.
|
|
if(i < 3)
|
|
{
|
|
Con_DWarning("Avoided reading garbage for \"%s\" \"%s\"\n", PR_GetString(key->s_name), s);
|
|
for(; i < 3; i++)
|
|
((float *)d)[i] = 0.0f;
|
|
}
|
|
break;
|
|
|
|
case ev_entity:
|
|
*(int32_t *)d = EDICT_TO_PROG(EDICT_NUM(atoi(s)));
|
|
break;
|
|
|
|
case ev_field:
|
|
def = ED_FindField(s);
|
|
if(!def)
|
|
{
|
|
//johnfitz -- HACK -- suppress error becuase fog/sky might not be mentioned in defs.qc
|
|
if(strncmp(s, "sky", 3) && strcmp(s, "fog"))
|
|
Con_DPrintf("Can't find field %s\n", s);
|
|
return false;
|
|
}
|
|
*(int32_t *)d = G_INT(def->ofs);
|
|
break;
|
|
|
|
case ev_function:
|
|
func = ED_FindFunction(s);
|
|
if(!func)
|
|
{
|
|
Con_Printf("Can't find function %s\n", s);
|
|
return false;
|
|
}
|
|
*(func_t *)d = func - pr_functions;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
====================
|
|
ED_ParseEdict
|
|
|
|
Parses an edict out of the given string, returning the new position
|
|
ed should be a properly initialized empty edict.
|
|
Used for initial level load and for savegames.
|
|
====================
|
|
*/
|
|
const char *ED_ParseEdict(const char *data, edict_t *ent)
|
|
{
|
|
ddef_t *key;
|
|
char keyname[256];
|
|
bool anglehack, init;
|
|
int32_t n;
|
|
|
|
init = false;
|
|
|
|
// clear it
|
|
if(ent != sv.edicts) // hack
|
|
memset(ent->fields, 0, progs.entityfields * 4);
|
|
|
|
// go through all the dictionary pairs
|
|
while(1)
|
|
{
|
|
// parse key
|
|
data = COM_Parse(data);
|
|
if(com_token[0] == '}')
|
|
break;
|
|
if(!data)
|
|
Host_Error("ED_ParseEntity: EOF without closing brace");
|
|
|
|
// anglehack is to allow QuakeEd to write single scalar angles
|
|
// and allow them to be turned into vectors. (FIXME...)
|
|
if(!strcmp(com_token, "angle"))
|
|
{
|
|
strcpy(com_token, "angles");
|
|
anglehack = true;
|
|
}
|
|
else
|
|
anglehack = false;
|
|
|
|
// FIXME: change light to _light to get rid of this hack
|
|
if(!strcmp(com_token, "light"))
|
|
strcpy(com_token, "light_lev"); // hack for single light def
|
|
|
|
q_strlcpy(keyname, com_token, sizeof(keyname));
|
|
|
|
// another hack to fix keynames with trailing spaces
|
|
n = strlen(keyname);
|
|
while(n && keyname[n - 1] == ' ')
|
|
{
|
|
keyname[n - 1] = 0;
|
|
n--;
|
|
}
|
|
|
|
// parse value
|
|
data = COM_Parse(data);
|
|
if(!data)
|
|
Host_Error("ED_ParseEntity: EOF without closing brace");
|
|
|
|
if(com_token[0] == '}')
|
|
Host_Error("ED_ParseEntity: closing brace without data");
|
|
|
|
init = true;
|
|
|
|
// keynames with a leading underscore are used for utility comments,
|
|
// and are immediately discarded by quake
|
|
if(keyname[0] == '_')
|
|
continue;
|
|
|
|
//johnfitz -- hack to support .alpha even when program doesn't know about it
|
|
if(!strcmp(keyname, "alpha"))
|
|
ent->alpha = ENTALPHA_ENCODE(atof(com_token));
|
|
//johnfitz
|
|
|
|
key = ED_FindField(keyname);
|
|
if(!key)
|
|
{
|
|
//johnfitz -- HACK -- suppress error becuase fog/sky/alpha might not be mentioned in defs.qc
|
|
if(strncmp(keyname, "sky", 3) && strcmp(keyname, "fog") && strcmp(keyname, "alpha"))
|
|
Con_DPrintf("\"%s\" is not a field\n", keyname); //johnfitz -- was Con_Printf
|
|
continue;
|
|
}
|
|
|
|
if(anglehack)
|
|
{
|
|
char temp[32];
|
|
strcpy(temp, com_token);
|
|
sprintf(com_token, "0 %s 0", temp);
|
|
}
|
|
|
|
if(!ED_ParseEpair(ent->fields, key, com_token))
|
|
Host_Error("ED_ParseEdict: parse error");
|
|
}
|
|
|
|
if(!init)
|
|
ent->free = true;
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
ED_LoadFromFile
|
|
|
|
The entities are directly placed in the array, rather than allocated with
|
|
ED_Alloc, because otherwise an error loading the map would have entity
|
|
number references out of order.
|
|
|
|
Creates a server's entity / program execution context by
|
|
parsing textual entity definitions out of an ent file.
|
|
|
|
Used for both fresh maps and savegame loads. A fresh map would also need
|
|
to call ED_CallSpawnFunctions () to let the objects initialize themselves.
|
|
================
|
|
*/
|
|
void ED_LoadFromFile(const char *data)
|
|
{
|
|
dfunction_t *func;
|
|
edict_t *ent = NULL;
|
|
int32_t inhibit = 0;
|
|
|
|
G_FLOAT(GBL_time) = sv.time;
|
|
|
|
// parse ents
|
|
while(1)
|
|
{
|
|
// parse the opening brace
|
|
data = COM_Parse(data);
|
|
if(!data)
|
|
break;
|
|
if(com_token[0] != '{')
|
|
Host_Error("ED_LoadFromFile: found %s when expecting {", com_token);
|
|
|
|
if(!ent)
|
|
ent = EDICT_NUM(0);
|
|
else
|
|
ent = ED_Alloc();
|
|
data = ED_ParseEdict(data, ent);
|
|
|
|
// remove things from different skill levels or deathmatch
|
|
if(deathmatch.value)
|
|
{
|
|
if(((int32_t)ED_FLOAT(ent, ED_spawnflags) & SPAWNFLAG_NOT_DEATHMATCH))
|
|
{
|
|
ED_Free(ent);
|
|
inhibit++;
|
|
continue;
|
|
}
|
|
}
|
|
else if((current_skill == 0 && ((int32_t)ED_FLOAT(ent, ED_spawnflags) & SPAWNFLAG_NOT_EASY))
|
|
|| (current_skill == 1 && ((int32_t)ED_FLOAT(ent, ED_spawnflags) & SPAWNFLAG_NOT_MEDIUM))
|
|
|| (current_skill >= 2 && ((int32_t)ED_FLOAT(ent, ED_spawnflags) & SPAWNFLAG_NOT_HARD)))
|
|
{
|
|
ED_Free(ent);
|
|
inhibit++;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// immediately call spawn function
|
|
//
|
|
if(!ED_RSTRING(ent, ED_classname))
|
|
{
|
|
Con_SafePrintf("No classname for:\n"); //johnfitz -- was Con_Printf
|
|
ED_Print(ent);
|
|
ED_Free(ent);
|
|
continue;
|
|
}
|
|
|
|
// look for the spawn function
|
|
func = ED_FindFunction(ED_STRING(ent, ED_classname));
|
|
|
|
if(!func)
|
|
{
|
|
Con_SafePrintf("No spawn function for:\n"); //johnfitz -- was Con_Printf
|
|
ED_Print(ent);
|
|
ED_Free(ent);
|
|
continue;
|
|
}
|
|
|
|
G_PEDICT(GBL_self) = EDICT_TO_PROG(ent);
|
|
PR_ExecuteProgram(func - pr_functions);
|
|
}
|
|
|
|
Con_DPrintf("%" PRIi32 " entities inhibited\n", inhibit);
|
|
}
|
|
|
|
void ED_Load(void)
|
|
{
|
|
int32_t i;
|
|
|
|
// flush the non-C variable lookup cache
|
|
for(i = 0; i < GEFV_CACHESIZE; i++)
|
|
gefvCache[i].field[0] = 0;
|
|
}
|
|
|
|
void ED_Init(void)
|
|
{
|
|
Cmd_AddCommand("edict", ED_PrintEdict_f);
|
|
Cmd_AddCommand("edicts", ED_PrintEdicts);
|
|
Cmd_AddCommand("edictcount", ED_Count);
|
|
}
|
|
|
|
edict_t *EDICT_NUM(int32_t n)
|
|
{
|
|
if(n < 0 || n >= sv.max_edicts)
|
|
Host_Error("EDICT_NUM: bad number %" PRIi32 "", n);
|
|
return PROG_TO_EDICT(n * pr_edict_size);
|
|
}
|
|
|
|
int32_t NUM_FOR_EDICT(edict_t *e)
|
|
{
|
|
int32_t b;
|
|
|
|
b = EDICT_TO_PROG(e);
|
|
b = b / pr_edict_size;
|
|
|
|
if(b < 0 || b >= sv.num_edicts)
|
|
Host_Error("NUM_FOR_EDICT: bad pointer");
|
|
return b;
|
|
}
|
|
|
|
pedict_t EDICT_TO_PROG(void *e)
|
|
{
|
|
return (byte *)e - (byte *)sv.edicts;
|
|
}
|
|
|
|
void *PROG_TO_EDICT(pedict_t e)
|
|
{
|
|
return (byte *)sv.edicts + e;
|
|
}
|
|
|