| /* |
| Copyright (C) 1996-1997 Id Software, Inc. |
| |
| 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 "quakedef.h" |
| |
| dprograms_t *progs; |
| dfunction_t *pr_functions; |
| char *pr_strings; |
| ddef_t *pr_fielddefs; |
| ddef_t *pr_globaldefs; |
| dstatement_t *pr_statements; |
| globalvars_t *pr_global_struct; |
| float *pr_globals; // same as pr_global_struct |
| int pr_edict_size; // in bytes |
| |
| unsigned short pr_crc; |
| |
| int type_size[8] = {1,sizeof(string_t)/4,1,3,1,1,sizeof(func_t)/4,sizeof(void *)/4}; |
| |
| ddef_t *ED_FieldAtOfs (int ofs); |
| qboolean ED_ParseEpair (void *base, ddef_t *key, char *s); |
| |
| cvar_t nomonsters = CVAR2("nomonsters", "0"); |
| cvar_t gamecfg = CVAR2("gamecfg", "0"); |
| cvar_t scratch1 = CVAR2("scratch1", "0"); |
| cvar_t scratch2 = CVAR2("scratch2", "0"); |
| cvar_t scratch3 = CVAR2("scratch3", "0"); |
| cvar_t scratch4 = CVAR2("scratch4", "0"); |
| cvar_t savedgamecfg = CVAR3("savedgamecfg", "0", true); |
| cvar_t saved1 = CVAR3("saved1", "0", true); |
| cvar_t saved2 = CVAR3("saved2", "0", true); |
| cvar_t saved3 = CVAR3("saved3", "0", true); |
| cvar_t saved4 = CVAR3("saved4", "0", true); |
| |
| #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->u.v, 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) |
| { |
| int 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 == MAX_EDICTS) |
| Sys_Error ("ED_Alloc: no free edicts"); |
| |
| sv.num_edicts++; |
| e = EDICT_NUM(i); |
| ED_ClearEdict (e); |
| |
| 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->u.v.model = 0; |
| ed->u.v.takedamage = 0; |
| ed->u.v.modelindex = 0; |
| ed->u.v.colormap = 0; |
| ed->u.v.skin = 0; |
| ed->u.v.frame = 0; |
| VectorCopy (vec3_origin, ed->u.v.origin); |
| VectorCopy (vec3_origin, ed->u.v.angles); |
| ed->u.v.nextthink = -1; |
| ed->u.v.solid = 0; |
| |
| ed->freetime = sv.time; |
| } |
| |
| //=========================================================================== |
| |
| /* |
| ============ |
| ED_GlobalAtOfs |
| ============ |
| */ |
| ddef_t *ED_GlobalAtOfs (int ofs) |
| { |
| ddef_t *def; |
| int 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 (int ofs) |
| { |
| ddef_t *def; |
| int i; |
| |
| for (i=0 ; i<progs->numfielddefs ; i++) |
| { |
| def = &pr_fielddefs[i]; |
| if (def->ofs == ofs) |
| return def; |
| } |
| return NULL; |
| } |
| |
| /* |
| ============ |
| ED_FindField |
| ============ |
| */ |
| ddef_t *ED_FindField (const char *name) |
| { |
| ddef_t *def; |
| int i; |
| |
| for (i=0 ; i<progs->numfielddefs ; i++) |
| { |
| def = &pr_fielddefs[i]; |
| if (!strcmp(pr_strings + def->s_name,name) ) |
| return def; |
| } |
| return NULL; |
| } |
| |
| |
| /* |
| ============ |
| ED_FindGlobal |
| ============ |
| */ |
| ddef_t *ED_FindGlobal (const char *name) |
| { |
| ddef_t *def; |
| int i; |
| |
| for (i=0 ; i<progs->numglobaldefs ; i++) |
| { |
| def = &pr_globaldefs[i]; |
| if (!strcmp(pr_strings + def->s_name,name) ) |
| return def; |
| } |
| return NULL; |
| } |
| |
| |
| /* |
| ============ |
| ED_FindFunction |
| ============ |
| */ |
| dfunction_t *ED_FindFunction (const char *name) |
| { |
| dfunction_t *func; |
| int i; |
| |
| for (i=0 ; i<progs->numfunctions ; i++) |
| { |
| func = &pr_functions[i]; |
| if (!strcmp(pr_strings + func->s_name,name) ) |
| return func; |
| } |
| return NULL; |
| } |
| |
| |
| eval_t *GetEdictFieldValue(edict_t *ed, const char *field) |
| { |
| ddef_t *def = NULL; |
| int i; |
| static int 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 (eval_t *)((char *)&ed->u.v + def->ofs*4); |
| } |
| |
| |
| /* |
| ============ |
| PR_ValueString |
| |
| Returns a string describing *data in a type specific manner |
| ============= |
| */ |
| char *PR_ValueString (etype_t type, eval_t *val) |
| { |
| static char line[256]; |
| ddef_t *def; |
| dfunction_t *f; |
| |
| type = (etype_t) (type & ~DEF_SAVEGLOBAL); |
| |
| switch (type) |
| { |
| case ev_string: |
| sprintf (line, "%s", pr_strings + val->string); |
| break; |
| case ev_entity: |
| sprintf (line, "entity %i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict)) ); |
| break; |
| case ev_function: |
| f = pr_functions + val->function; |
| sprintf (line, "%s()", pr_strings + f->s_name); |
| break; |
| case ev_field: |
| def = ED_FieldAtOfs ( val->_int ); |
| sprintf (line, ".%s", pr_strings + def->s_name); |
| break; |
| case ev_void: |
| sprintf (line, "void"); |
| break; |
| case ev_float: |
| sprintf (line, "%5.1f", val->_float); |
| break; |
| case ev_vector: |
| sprintf (line, "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]); |
| break; |
| case ev_pointer: |
| sprintf (line, "pointer"); |
| break; |
| default: |
| sprintf (line, "bad type %i", type); |
| break; |
| } |
| |
| return line; |
| } |
| |
| /* |
| ============ |
| PR_UglyValueString |
| |
| Returns a string describing *data in a type specific manner |
| Easier to parse than PR_ValueString |
| ============= |
| */ |
| char *PR_UglyValueString (etype_t type, eval_t *val) |
| { |
| static char line[256]; |
| ddef_t *def; |
| dfunction_t *f; |
| |
| type = (etype_t) (type & ~DEF_SAVEGLOBAL); |
| |
| switch (type) |
| { |
| case ev_string: |
| sprintf (line, "%s", pr_strings + val->string); |
| break; |
| case ev_entity: |
| sprintf (line, "%i", NUM_FOR_EDICT(PROG_TO_EDICT(val->edict))); |
| break; |
| case ev_function: |
| f = pr_functions + val->function; |
| sprintf (line, "%s", pr_strings + f->s_name); |
| break; |
| case ev_field: |
| def = ED_FieldAtOfs ( val->_int ); |
| sprintf (line, "%s", pr_strings + def->s_name); |
| break; |
| case ev_void: |
| sprintf (line, "void"); |
| break; |
| case ev_float: |
| sprintf (line, "%f", val->_float); |
| break; |
| case ev_vector: |
| sprintf (line, "%f %f %f", val->vector[0], val->vector[1], val->vector[2]); |
| break; |
| default: |
| sprintf (line, "bad type %i", type); |
| break; |
| } |
| |
| return line; |
| } |
| |
| /* |
| ============ |
| PR_GlobalString |
| |
| Returns a string with a description and the contents of a global, |
| padded to 20 field width |
| ============ |
| */ |
| char *PR_GlobalString (int ofs) |
| { |
| char *s; |
| int i; |
| ddef_t *def; |
| void *val; |
| static char line[128]; |
| |
| val = (void *)&pr_globals[ofs]; |
| def = ED_GlobalAtOfs(ofs); |
| if (!def) |
| sprintf (line,"%i(??""?)", ofs); |
| else |
| { |
| s = PR_ValueString ((etype_t) def->type, (eval_t*) val); |
| sprintf (line,"%i(%s)%s", ofs, pr_strings + def->s_name, s); |
| } |
| |
| i = strlen(line); |
| for ( ; i<20 ; i++) |
| strcat (line," "); |
| strcat (line," "); |
| |
| return line; |
| } |
| |
| char *PR_GlobalStringNoContents (int ofs) |
| { |
| int i; |
| ddef_t *def; |
| static char line[128]; |
| |
| def = ED_GlobalAtOfs(ofs); |
| if (!def) |
| sprintf (line,"%i(??""?)", ofs); |
| else |
| sprintf (line,"%i(%s)", ofs, pr_strings + def->s_name); |
| |
| i = strlen(line); |
| for ( ; i<20 ; i++) |
| strcat (line," "); |
| strcat (line," "); |
| |
| return line; |
| } |
| |
| |
| /* |
| ============= |
| ED_Print |
| |
| For debugging |
| ============= |
| */ |
| void ED_Print (edict_t *ed) |
| { |
| int l; |
| ddef_t *d; |
| int *v; |
| int i, j; |
| char *name; |
| int type; |
| |
| if (ed->free) |
| { |
| Con_Printf ("FREE\n"); |
| return; |
| } |
| |
| Con_Printf("\nEDICT %i:\n", NUM_FOR_EDICT(ed)); |
| for (i=1 ; i<progs->numfielddefs ; i++) |
| { |
| d = &pr_fielddefs[i]; |
| name = pr_strings + d->s_name; |
| if (name[strlen(name)-2] == '_') |
| continue; // skip _x, _y, _z vars |
| |
| v = (int *)((char *)&ed->u.v + d->ofs*4); |
| |
| // 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_Printf ("%s",name); |
| l = strlen (name); |
| while (l++ < 15) |
| Con_Printf (" "); |
| |
| Con_Printf ("%s\n", PR_ValueString((etype_t) d->type, (eval_t *)v)); |
| } |
| } |
| |
| /* |
| ============= |
| ED_Write |
| |
| For savegames |
| ============= |
| */ |
| void ED_Write (FILE *f, edict_t *ed) |
| { |
| ddef_t *d; |
| int *v; |
| int i, j; |
| char *name; |
| int type; |
| |
| fprintf (f, "{\n"); |
| |
| if (ed->free) |
| { |
| fprintf (f, "}\n"); |
| return; |
| } |
| |
| for (i=1 ; i<progs->numfielddefs ; i++) |
| { |
| d = &pr_fielddefs[i]; |
| name = pr_strings + d->s_name; |
| if (name[strlen(name)-2] == '_') |
| continue; // skip _x, _y, _z vars |
| |
| v = (int *)((char *)&ed->u.v + d->ofs*4); |
| |
| // 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((etype_t) d->type, (eval_t *)v)); |
| } |
| |
| fprintf (f, "}\n"); |
| } |
| |
| void ED_PrintNum (int ent) |
| { |
| ED_Print (EDICT_NUM(ent)); |
| } |
| |
| /* |
| ============= |
| ED_PrintEdicts |
| |
| For debugging, prints all the entities in the current server |
| ============= |
| */ |
| void ED_PrintEdicts (void) |
| { |
| int i; |
| |
| Con_Printf ("%i 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 |
| ============= |
| */ |
| void ED_PrintEdict_f (void) |
| { |
| int i; |
| |
| i = Q_atoi (Cmd_Argv(1)); |
| if (i >= sv.num_edicts) |
| { |
| Con_Printf("Bad edict number\n"); |
| return; |
| } |
| ED_PrintNum (i); |
| } |
| |
| /* |
| ============= |
| ED_Count |
| |
| For debugging |
| ============= |
| */ |
| void ED_Count (void) |
| { |
| int i; |
| edict_t *ent; |
| int active, models, solid, step; |
| |
| active = models = solid = step = 0; |
| for (i=0 ; i<sv.num_edicts ; i++) |
| { |
| ent = EDICT_NUM(i); |
| if (ent->free) |
| continue; |
| active++; |
| if (ent->u.v.solid) |
| solid++; |
| if (ent->u.v.model) |
| models++; |
| if (ent->u.v.movetype == MOVETYPE_STEP) |
| step++; |
| } |
| |
| Con_Printf ("num_edicts:%3i\n", sv.num_edicts); |
| Con_Printf ("active :%3i\n", active); |
| Con_Printf ("view :%3i\n", models); |
| Con_Printf ("touch :%3i\n", solid); |
| Con_Printf ("step :%3i\n", step); |
| |
| } |
| |
| /* |
| ============================================================================== |
| |
| ARCHIVING GLOBALS |
| |
| FIXME: need to tag constants, doesn't really work |
| ============================================================================== |
| */ |
| |
| /* |
| ============= |
| ED_WriteGlobals |
| ============= |
| */ |
| void ED_WriteGlobals (FILE *f) |
| { |
| ddef_t *def; |
| int i; |
| char *name; |
| int 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_strings + def->s_name; |
| fprintf (f,"\"%s\" ", name); |
| fprintf (f,"\"%s\"\n", PR_UglyValueString((etype_t) type, (eval_t *)&pr_globals[def->ofs])); |
| } |
| fprintf (f,"}\n"); |
| } |
| |
| /* |
| ============= |
| ED_ParseGlobals |
| ============= |
| */ |
| void ED_ParseGlobals (char *data) |
| { |
| char keyname[64]; |
| ddef_t *key; |
| |
| while (1) |
| { |
| // parse key |
| data = COM_Parse (data); |
| if (com_token[0] == '}') |
| break; |
| if (!data) |
| Sys_Error ("ED_ParseEntity: EOF without closing brace"); |
| |
| strcpy (keyname, com_token); |
| |
| // parse value |
| data = COM_Parse (data); |
| if (!data) |
| Sys_Error ("ED_ParseEntity: EOF without closing brace"); |
| |
| if (com_token[0] == '}') |
| Sys_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 ((void *)pr_globals, key, com_token)) |
| Host_Error ("ED_ParseGlobals: parse error"); |
| } |
| } |
| |
| //============================================================================ |
| |
| |
| /* |
| ============= |
| ED_NewString |
| ============= |
| */ |
| char *ED_NewString (char *string) |
| { |
| char *new_, *new_p; |
| int i,l; |
| |
| l = strlen(string) + 1; |
| new_ = (char*) Hunk_Alloc (l); |
| new_p = new_; |
| |
| 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 new_; |
| } |
| |
| |
| /* |
| ============= |
| ED_ParseEval |
| |
| Can parse either fields or globals |
| returns false if error |
| ============= |
| */ |
| qboolean ED_ParseEpair (void *base, ddef_t *key, char *s) |
| { |
| int i; |
| char string[128]; |
| ddef_t *def; |
| char *v, *w; |
| void *d; |
| dfunction_t *func; |
| |
| d = (void *)((int *)base + key->ofs); |
| |
| switch (key->type & ~DEF_SAVEGLOBAL) |
| { |
| case ev_string: |
| *(string_t *)d = ED_NewString (s) - pr_strings; |
| break; |
| |
| case ev_float: |
| *(float *)d = atof (s); |
| break; |
| |
| case ev_vector: |
| strcpy (string, s); |
| v = string; |
| w = string; |
| for (i=0 ; i<3 ; i++) |
| { |
| while (*v && *v != ' ') |
| v++; |
| *v = 0; |
| ((float *)d)[i] = atof (w); |
| w = v = v+1; |
| } |
| break; |
| |
| case ev_entity: |
| *(int *)d = EDICT_TO_PROG(EDICT_NUM(atoi (s))); |
| break; |
| |
| case ev_field: |
| def = ED_FindField (s); |
| if (!def) |
| { |
| Con_Printf ("Can't find field %s\n", s); |
| return false; |
| } |
| *(int *)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. |
| ==================== |
| */ |
| char *ED_ParseEdict (char *data, edict_t *ent) |
| { |
| ddef_t *key; |
| qboolean anglehack; |
| qboolean init; |
| char keyname[256]; |
| int n; |
| |
| init = false; |
| |
| // clear it |
| if (ent != sv.edicts) // hack |
| memset (&ent->u.v, 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) |
| Sys_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 |
| |
| strcpy (keyname, com_token); |
| |
| // another hack to fix heynames with trailing spaces |
| n = strlen(keyname); |
| while (n && keyname[n-1] == ' ') |
| { |
| keyname[n-1] = 0; |
| n--; |
| } |
| |
| // parse value |
| data = COM_Parse (data); |
| if (!data) |
| Sys_Error ("ED_ParseEntity: EOF without closing brace"); |
| |
| if (com_token[0] == '}') |
| Sys_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; |
| |
| key = ED_FindField (keyname); |
| if (!key) |
| { |
| Con_Printf ("'%s' is not a field\n", keyname); |
| continue; |
| } |
| |
| if (anglehack) |
| { |
| char temp[32]; |
| strcpy (temp, com_token); |
| sprintf (com_token, "0 %s 0", temp); |
| } |
| |
| if (!ED_ParseEpair ((void *)&ent->u.v, 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 (char *data) |
| { |
| edict_t *ent; |
| int inhibit; |
| dfunction_t *func; |
| |
| ent = NULL; |
| inhibit = 0; |
| pr_global_struct->time = sv.time; |
| |
| // parse ents |
| while (1) |
| { |
| // parse the opening brace |
| data = COM_Parse (data); |
| if (!data) |
| break; |
| if (com_token[0] != '{') |
| Sys_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 (((int)ent->u.v.spawnflags & SPAWNFLAG_NOT_DEATHMATCH)) |
| { |
| ED_Free (ent); |
| inhibit++; |
| continue; |
| } |
| } |
| else if ((current_skill == 0 && ((int)ent->u.v.spawnflags & SPAWNFLAG_NOT_EASY)) |
| || (current_skill == 1 && ((int)ent->u.v.spawnflags & SPAWNFLAG_NOT_MEDIUM)) |
| || (current_skill >= 2 && ((int)ent->u.v.spawnflags & SPAWNFLAG_NOT_HARD)) ) |
| { |
| ED_Free (ent); |
| inhibit++; |
| continue; |
| } |
| |
| // |
| // immediately call spawn function |
| // |
| if (!ent->u.v.classname) |
| { |
| Con_Printf ("No classname for:\n"); |
| ED_Print (ent); |
| ED_Free (ent); |
| continue; |
| } |
| |
| // look for the spawn function |
| func = ED_FindFunction ( pr_strings + ent->u.v.classname ); |
| |
| if (!func) |
| { |
| Con_Printf ("No spawn function for:\n"); |
| ED_Print (ent); |
| ED_Free (ent); |
| continue; |
| } |
| |
| pr_global_struct->self = EDICT_TO_PROG(ent); |
| PR_ExecuteProgram (func - pr_functions); |
| } |
| |
| Con_DPrintf ("%i entities inhibited\n", inhibit); |
| } |
| |
| |
| /* |
| =============== |
| PR_LoadProgs |
| =============== |
| */ |
| void PR_LoadProgs (void) |
| { |
| int i; |
| |
| // flush the non-C variable lookup cache |
| for (i=0 ; i<GEFV_CACHESIZE ; i++) |
| gefvCache[i].field[0] = 0; |
| |
| CRC_Init (&pr_crc); |
| |
| progs = (dprograms_t *)COM_LoadHunkFile ("progs.dat"); |
| if (!progs) |
| Sys_Error ("PR_LoadProgs: couldn't load progs.dat"); |
| Con_DPrintf ("Programs occupy %iK.\n", com_filesize/1024); |
| |
| for (i=0 ; i<com_filesize ; i++) |
| CRC_ProcessByte (&pr_crc, ((byte *)progs)[i]); |
| |
| // byte swap the header |
| for (i=0 ; i< (int) (sizeof(*progs)/4) ; i++) |
| ((int *)progs)[i] = LittleLong ( ((int *)progs)[i] ); |
| |
| if (progs->version != PROG_VERSION) |
| Sys_Error ("progs.dat has wrong version number (%i should be %i)", progs->version, PROG_VERSION); |
| if (progs->crc != PROGHEADER_CRC) |
| Sys_Error ("progs.dat system vars have been modified, progdefs.h is out of date"); |
| |
| pr_functions = (dfunction_t *)((byte *)progs + progs->ofs_functions); |
| pr_strings = (char *)progs + progs->ofs_strings; |
| pr_globaldefs = (ddef_t *)((byte *)progs + progs->ofs_globaldefs); |
| pr_fielddefs = (ddef_t *)((byte *)progs + progs->ofs_fielddefs); |
| pr_statements = (dstatement_t *)((byte *)progs + progs->ofs_statements); |
| |
| pr_global_struct = (globalvars_t *)((byte *)progs + progs->ofs_globals); |
| pr_globals = (float *)pr_global_struct; |
| |
| pr_edict_size = progs->entityfields * 4 + sizeof (edict_t) - sizeof(entvars_t); |
| |
| // byte swap the lumps |
| for (i=0 ; i<progs->numstatements ; i++) |
| { |
| pr_statements[i].op = LittleShort(pr_statements[i].op); |
| pr_statements[i].a = LittleShort(pr_statements[i].a); |
| pr_statements[i].b = LittleShort(pr_statements[i].b); |
| pr_statements[i].c = LittleShort(pr_statements[i].c); |
| } |
| |
| for (i=0 ; i<progs->numfunctions; i++) |
| { |
| pr_functions[i].first_statement = LittleLong (pr_functions[i].first_statement); |
| pr_functions[i].parm_start = LittleLong (pr_functions[i].parm_start); |
| pr_functions[i].s_name = LittleLong (pr_functions[i].s_name); |
| pr_functions[i].s_file = LittleLong (pr_functions[i].s_file); |
| pr_functions[i].numparms = LittleLong (pr_functions[i].numparms); |
| pr_functions[i].locals = LittleLong (pr_functions[i].locals); |
| } |
| |
| for (i=0 ; i<progs->numglobaldefs ; i++) |
| { |
| pr_globaldefs[i].type = LittleShort (pr_globaldefs[i].type); |
| pr_globaldefs[i].ofs = LittleShort (pr_globaldefs[i].ofs); |
| pr_globaldefs[i].s_name = LittleLong (pr_globaldefs[i].s_name); |
| } |
| |
| for (i=0 ; i<progs->numfielddefs ; i++) |
| { |
| pr_fielddefs[i].type = LittleShort (pr_fielddefs[i].type); |
| if (pr_fielddefs[i].type & DEF_SAVEGLOBAL) |
| Sys_Error ("PR_LoadProgs: pr_fielddefs[i].type & DEF_SAVEGLOBAL"); |
| pr_fielddefs[i].ofs = LittleShort (pr_fielddefs[i].ofs); |
| pr_fielddefs[i].s_name = LittleLong (pr_fielddefs[i].s_name); |
| } |
| |
| for (i=0 ; i<progs->numglobals ; i++) |
| ((int *)pr_globals)[i] = LittleLong (((int *)pr_globals)[i]); |
| } |
| |
| |
| /* |
| =============== |
| PR_Init |
| =============== |
| */ |
| void PR_Init (void) |
| { |
| Cmd_AddCommand ("edict", ED_PrintEdict_f); |
| Cmd_AddCommand ("edicts", ED_PrintEdicts); |
| Cmd_AddCommand ("edictcount", ED_Count); |
| Cmd_AddCommand ("profile", PR_Profile_f); |
| Cvar_RegisterVariable (&nomonsters); |
| Cvar_RegisterVariable (&gamecfg); |
| Cvar_RegisterVariable (&scratch1); |
| Cvar_RegisterVariable (&scratch2); |
| Cvar_RegisterVariable (&scratch3); |
| Cvar_RegisterVariable (&scratch4); |
| Cvar_RegisterVariable (&savedgamecfg); |
| Cvar_RegisterVariable (&saved1); |
| Cvar_RegisterVariable (&saved2); |
| Cvar_RegisterVariable (&saved3); |
| Cvar_RegisterVariable (&saved4); |
| } |
| |
| |
| |
| edict_t *EDICT_NUM(int n) |
| { |
| if (n < 0 || n >= sv.max_edicts) |
| Sys_Error ("EDICT_NUM: bad number %i", n); |
| return (edict_t *)((byte *)sv.edicts+ (n)*pr_edict_size); |
| } |
| |
| int NUM_FOR_EDICT(edict_t *e) |
| { |
| int b; |
| |
| b = (byte *)e - (byte *)sv.edicts; |
| b = b / pr_edict_size; |
| |
| if (b < 0 || b >= sv.num_edicts) |
| Sys_Error ("NUM_FOR_EDICT: bad pointer"); |
| return b; |
| } |