From 952c5c128f9efaea89d41d882c4ea3ade7df4591 Mon Sep 17 00:00:00 2001 From: zakk Date: Fri, 26 Aug 2005 04:48:05 +0000 Subject: Itsa me, quake3io! git-svn-id: svn://svn.icculus.org/quake3/trunk@2 edf5b092-35ff-0310-97b2-ce42778d08ea --- q3radiant/ENTITY.CPP | 933 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 933 insertions(+) create mode 100755 q3radiant/ENTITY.CPP (limited to 'q3radiant/ENTITY.CPP') diff --git a/q3radiant/ENTITY.CPP b/q3radiant/ENTITY.CPP new file mode 100755 index 0000000..d4353b5 --- /dev/null +++ b/q3radiant/ENTITY.CPP @@ -0,0 +1,933 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code 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. + +Quake III Arena source code 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 Foobar; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "stdafx.h" +#include "qe3.h" + +// +int g_entityId = 1; + +char *ValueForKey ( epair_t *&e, const char *key) +{ + epair_t *ep; + for (ep=e ; ep ; ep=ep->next) + { + if (!strcmp (ep->key, key) ) + { + return ep->value; + } + } + return ""; +} + + +char *ValueForKey (entity_t *ent, const char *key) +{ + return ValueForKey(ent->epairs, key); +} + +void TrackMD3Angles(entity_t *e, const char *key, const char *value) +{ + if (strcmpi(key, "angle") != 0) + { + return; + } + + if (e->eclass->fixedsize && e->eclass->nShowFlags & ECLASS_MISCMODEL) + { + float a = FloatForKey (e, "angle"); + float b = atof(value); + if (a != b) + { + vec3_t vAngle; + vAngle[0] = vAngle[1] = 0; + vAngle[2] = -a; + Brush_Rotate(e->brushes.onext, vAngle, e->origin, true); + vAngle[2] = b; + Brush_Rotate(e->brushes.onext, vAngle, e->origin, true); + } + } +} + +void SetKeyValue (epair_t *&e, const char *key, const char *value) +{ + epair_t *ep; + for (ep=e ; ep ; ep=ep->next) + { + if (!strcmp (ep->key, key) ) + { + free (ep->value); + ep->value = (char*)qmalloc(strlen(value)+1); + strcpy (ep->value, value); + return; + } + } + ep = (epair_t*)qmalloc (sizeof(*ep)); + ep->next = e; + e = ep; + ep->key = (char*)qmalloc(strlen(key)+1); + strcpy (ep->key, key); + ep->value = (char*)qmalloc(strlen(value)+1); + strcpy (ep->value, value); + +} + + +void SetKeyValue (entity_t *ent, const char *key, const char *value) +{ + + if (ent == NULL) + return; + + if (!key || !key[0]) + return; + + TrackMD3Angles(ent, key, value); + + SetKeyValue(ent->epairs, key, value); + +} + +void DeleteKey (epair_t *&e, const char *key) +{ + epair_t **ep, *next; + + ep = &e; + while (*ep) + { + next = *ep; + if ( !strcmp (next->key, key) ) + { + *ep = next->next; + free(next->key); + free(next->value); + free(next); + return; + } + ep = &next->next; + } +} + + +void DeleteKey (entity_t *ent, const char *key) +{ + DeleteKey(ent->epairs, key); +} + + + + +float FloatForKey (entity_t *ent, const char *key) +{ + char *k; + + k = ValueForKey (ent, key); + return atof(k); +} + +int IntForKey (entity_t *ent, const char *key) +{ + char *k; + + k = ValueForKey (ent, key); + return atoi(k); +} + +void GetVectorForKey (entity_t *ent, const char *key, vec3_t vec) +{ + char *k; + + k = ValueForKey (ent, key); + sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]); +} + +/* +=============== +Entity_FreeEpairs + +Frees the entity epairs. +=============== +*/ +void Entity_FreeEpairs(entity_t *e) +{ + epair_t *ep, *next; + + for (ep = e->epairs; ep; ep = next) + { + next = ep->next; + free (ep->key); + free (ep->value); + free (ep); + } + e->epairs = NULL; +} + +/* +=========== +Entity_AddToList +=========== +*/ +void Entity_AddToList(entity_t *e, entity_t *list) +{ + if (e->next || e->prev) + Error ("Entity_AddToList: allready linked"); + e->next = list->next; + list->next->prev = e; + list->next = e; + e->prev = list; +} + +/* +=========== +Entity_RemoveFromList +=========== +*/ +void Entity_RemoveFromList (entity_t *e) +{ + if (!e->next || !e->prev) + Error ("Entity_RemoveFromList: not linked"); + e->next->prev = e->prev; + e->prev->next = e->next; + e->next = e->prev = NULL; +} + + + +/* +=============== +Entity_Free + +Frees the entity and any brushes is has. +The entity is removed from the global entities list. +=============== +*/ +void Entity_Free (entity_t *e) +{ + // do we have a plugin entity ? + if ( e->pPlugEnt ) + { + e->pPlugEnt->DecRef(); + e->pPlugEnt = NULL; + } + + while (e->brushes.onext != &e->brushes) + Brush_Free (e->brushes.onext); + + if (e->next) + { + e->next->prev = e->prev; + e->prev->next = e->next; + } + + Entity_FreeEpairs(e); + + free (e); +} + +/* +================= +Entity_MemorySize +================= +*/ +int Entity_MemorySize(entity_t *e) +{ + epair_t *ep; + int size = 0; + + for (ep = e->epairs; ep; ep = ep->next) + { + size += _msize(ep->key); + size += _msize(ep->value); + size += _msize(ep); + } + size += _msize(e); + return size; +} + +/* +================= +ParseEpair +================= +*/ +epair_t *ParseEpair (void) +{ + epair_t *e; + + e = (epair_t*)qmalloc (sizeof(*e)); + + e->key = (char*)qmalloc(strlen(token)+1); + strcpy (e->key, token); + + GetToken (false); + e->value = (char*)qmalloc(strlen(token)+1); + strcpy (e->value, token); + + return e; +} + +/* +================ +Entity_Parse + +If onlypairs is set, the classname info will not +be looked up, and the entity will not be added +to the global list. Used for parsing the project. +================ +*/ +entity_t *Entity_Parse (qboolean onlypairs, brush_t* pList) +{ + entity_t *ent; + eclass_t *e; + brush_t *b; + vec3_t mins, maxs; + epair_t *ep; + qboolean has_brushes; + + if (!GetToken (true)) + return NULL; + + if (strcmp (token, "{") ) + Error ("ParseEntity: { not found"); + + ent = (entity_t*)qmalloc (sizeof(*ent)); + ent->entityId = g_entityId++; + ent->brushes.onext = ent->brushes.oprev = &ent->brushes; + + int n = 0; + do + { + if (!GetToken (true)) + { + Warning ("ParseEntity: EOF without closing brace"); + return NULL; + } + if (!strcmp (token, "}") ) + break; + if (!strcmp (token, "{") ) + { + b = Brush_Parse (); + if (b != NULL) + { + b->owner = ent; + // add to the end of the entity chain + b->onext = &ent->brushes; + b->oprev = ent->brushes.oprev; + ent->brushes.oprev->onext = b; + ent->brushes.oprev = b; + } + else + { + break; + } + } + else + { + ep = ParseEpair (); + ep->next = ent->epairs; + ent->epairs = ep; + } + } while (1); + + // group info entity? + if (strcmp(ValueForKey (ent, "classname"), "group_info") == 0) + return ent; + + if (onlypairs) + return ent; + + if (ent->brushes.onext == &ent->brushes) + has_brushes = false; + else + has_brushes = true; + + GetVectorForKey (ent, "origin", ent->origin); + + e = Eclass_ForName (ValueForKey (ent, "classname"), has_brushes); + ent->eclass = e; + if ( e->nShowFlags & ECLASS_PLUGINENTITY ) + { + // locate the plugin + CPlugIn * pPlug = g_pParentWnd->GetPlugInMgr().PluginForModule( e->hPlug ); + if (pPlug) + { + // create the plugin entity + IPluginEntity* pPlugEnt = pPlug->CreatePluginEntity( ent ); + if (pPlugEnt) + { + ent->pPlugEnt = pPlugEnt; + // the brush is used to select and move + pPlugEnt->GetBounds( mins, maxs ); + } + else + { + // give it a default bounding box + SetKeyValue (ent, "model", ""); + mins[0] = -4; mins[1] = -4; mins[2] = -4; + maxs[0] = 4; maxs[1] = 4; maxs[2] = 4; + VectorAdd( mins, ent->origin, mins ); + VectorAdd( maxs, ent->origin, maxs ); + } + b = Brush_Create (mins, maxs, &ent->eclass->texdef); + Entity_LinkBrush (ent, b); + Brush_Build( b, true ); + } + else + Sys_Printf("WARNING: plugin lookup failed for plugin entities\n"); + } + else if (e->fixedsize) + { // fixed size entity + if (ent->brushes.onext != &ent->brushes) + { + printf ("Warning: Fixed size entity with brushes\n"); +#if 0 + while (ent->brushes.onext != &ent->brushes) + { // FIXME: this will free the entity and crash! + Brush_Free (b); + } +#endif + ent->brushes.next = ent->brushes.prev = &ent->brushes; + } + + // create a custom brush + VectorAdd (e->mins, ent->origin, mins); + VectorAdd (e->maxs, ent->origin, maxs); + + float a = 0; + if (e->nShowFlags & ECLASS_MISCMODEL) + { + char* p = ValueForKey(ent, "model"); + if (p != NULL && strlen(p) > 0) + { + vec3_t vMin, vMax; + a = FloatForKey (ent, "angle"); + if (GetCachedModel(ent, p, vMin, vMax)) + { + // create a custom brush + VectorAdd (ent->md3Class->mins, ent->origin, mins); + VectorAdd (ent->md3Class->maxs, ent->origin, maxs); + } + } + } + + b = Brush_Create (mins, maxs, &e->texdef); + + if (a) + { + vec3_t vAngle; + vAngle[0] = vAngle[1] = 0; + vAngle[2] = a; + Brush_Rotate(b, vAngle, ent->origin, false); + } + + + b->owner = ent; + + b->onext = ent->brushes.onext; + b->oprev = &ent->brushes; + ent->brushes.onext->oprev = b; + ent->brushes.onext = b; + } + else + { // brush entity + if (ent->brushes.next == &ent->brushes) + printf ("Warning: Brush entity with no brushes\n"); + } + + // add all the brushes to the main list + if (pList) + { + for (b=ent->brushes.onext ; b != &ent->brushes ; b=b->onext) + { + b->next = pList->next; + pList->next->prev = b; + b->prev = pList; + pList->next = b; + } + } + + return ent; +} + +void VectorMidpoint(vec3_t va, vec3_t vb, vec3_t& out) +{ + for (int i = 0; i < 3; i++) + out[i] = va[i] + ((vb[i] - va[i]) / 2); +} + + +/* +============ +Entity_Write +============ +*/ +void Entity_Write (entity_t *e, FILE *f, qboolean use_region) +{ + epair_t *ep; + brush_t *b; + vec3_t origin; + char text[128]; + int count; + + // if none of the entities brushes are in the region, + // don't write the entity at all + if (use_region) + { + // in region mode, save the camera position as playerstart + if ( !strcmp(ValueForKey (e, "classname"), "info_player_start") ) + { + fprintf (f, "{\n"); + fprintf (f, "\"classname\" \"info_player_start\"\n"); + fprintf (f, "\"origin\" \"%i %i %i\"\n", (int)g_pParentWnd->GetCamera()->Camera().origin[0], + (int)g_pParentWnd->GetCamera()->Camera().origin[1], (int)g_pParentWnd->GetCamera()->Camera().origin[2]); + fprintf (f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamera()->Camera().angles[YAW]); + fprintf (f, "}\n"); + return; + } + + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + if (!Map_IsBrushFiltered(b)) + break; // got one + + if (b == &e->brushes) + return; // nothing visible + } + + if ( e->eclass->nShowFlags & ECLASS_PLUGINENTITY ) + { + // NOTE: the whole brush placement / origin stuff is a mess + VectorCopy( e->origin, origin ); + sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (e, "origin", text); + } + // if fixedsize, calculate a new origin based on the current + // brush position + else if (e->eclass->fixedsize) + { + if (e->eclass->nShowFlags & ECLASS_MISCMODEL && e->md3Class != NULL) + { + VectorCopy(e->origin, origin); + //VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin); + } + else + { + VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin); + } + sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (e, "origin", text); + } + + fprintf (f, "{\n"); + for (ep = e->epairs ; ep ; ep=ep->next) + fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value); + + if (!e->eclass->fixedsize) + { + count = 0; + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + { + if (!use_region || !Map_IsBrushFiltered (b)) + { + fprintf (f, "// brush %i\n", count); + count++; + Brush_Write (b, f); + } + } + } + fprintf (f, "}\n"); +} + + + +qboolean IsBrushSelected(brush_t* bSel) +{ + for (brush_t* b = selected_brushes.next ;b != NULL && b != &selected_brushes; b = b->next) + { + if (b == bSel) + return true; + } + return false; +} + +// +//============ +//Entity_WriteSelected +//============ +// +void Entity_WriteSelected(entity_t *e, FILE *f) +{ + epair_t *ep; + brush_t *b; + vec3_t origin; + char text[128]; + int count; + + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + if (IsBrushSelected(b)) + break; // got one + + if (b == &e->brushes) + return; // nothing selected + + // if fixedsize, calculate a new origin based on the current + // brush position + if (e->eclass->fixedsize) + { + if (e->eclass->nShowFlags & ECLASS_MISCMODEL && e->md3Class != NULL) + { + VectorCopy(e->origin, origin); + //VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin); + } + else + { + VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin); + } + sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (e, "origin", text); + } + + fprintf (f, "{\n"); + for (ep = e->epairs ; ep ; ep=ep->next) + fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value); + + if (!e->eclass->fixedsize) + { + count = 0; + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + { + if (IsBrushSelected(b)) + { + fprintf (f, "// brush %i\n", count); + count++; + Brush_Write (b, f); + } + } + } + fprintf (f, "}\n"); +} + + +// +//============ +//Entity_WriteSelected to a CMemFile +//============ +// +void Entity_WriteSelected(entity_t *e, CMemFile* pMemFile) +{ + epair_t *ep; + brush_t *b; + vec3_t origin; + char text[128]; + int count; + + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + if (IsBrushSelected(b)) + break; // got one + + if (b == &e->brushes) + return; // nothing selected + + // if fixedsize, calculate a new origin based on the current + // brush position + if (e->eclass->fixedsize) + { + if (e->eclass->nShowFlags & ECLASS_MISCMODEL && e->md3Class != NULL) + { + //VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin); + VectorCopy(e->origin, origin); + } + else + { + VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin); + } + sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]); + SetKeyValue (e, "origin", text); + } + + MemFile_fprintf(pMemFile, "{\n"); + for (ep = e->epairs ; ep ; ep=ep->next) + MemFile_fprintf(pMemFile, "\"%s\" \"%s\"\n", ep->key, ep->value); + + if (!e->eclass->fixedsize) + { + count = 0; + for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext) + { + if (IsBrushSelected(b)) + { + MemFile_fprintf(pMemFile, "// brush %i\n", count); + count++; + Brush_Write (b, pMemFile); + } + } + } + MemFile_fprintf(pMemFile, "}\n"); +} + + + + +/* +============ +Entity_Create + +Creates a new entity out of the selected_brushes list. +If the entity class is fixed size, the brushes are only +used to find a midpoint. Otherwise, the brushes have +their ownership transfered to the new entity. +============ +*/ +entity_t *Entity_Create (eclass_t *c) +{ + entity_t *e; + brush_t *b; + vec3_t mins, maxs; + int i; + + // check to make sure the brushes are ok + + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + if (b->owner != world_entity) + { + Sys_Printf ("Entity NOT created, brushes not all from world\n"); + Sys_Beep (); + return NULL; + } + } + + // create it + + e = (entity_t*)qmalloc(sizeof(*e)); + e->entityId = g_entityId++; + e->brushes.onext = e->brushes.oprev = &e->brushes; + e->eclass = c; + SetKeyValue (e, "classname", c->name); + + // add the entity to the entity list + Entity_AddToList(e, &entities); + + // plugin entity ? + if (c->nShowFlags & ECLASS_PLUGINENTITY) + { + // locate the plugin + CPlugIn * pPlug = g_pParentWnd->GetPlugInMgr().PluginForModule( c->hPlug ); + if (pPlug) + { + // + // just use the selection for positioning + // + b = selected_brushes.next; + for (i=0 ; i<3 ; i++) + e->origin[i] = b->mins[i] - c->mins[i]; + + // create the plugin entity + IPluginEntity* pPlugEnt = pPlug->CreatePluginEntity( e ); + + if (pPlugEnt) + { + e->pPlugEnt = pPlugEnt; + // the brush is used to select and move + pPlugEnt->GetBounds( mins, maxs ); + b = Brush_Create (mins, maxs, &c->texdef); + + Entity_LinkBrush (e, b); + + // delete the current selection + Select_Delete (); + + // select the new brush + b->next = b->prev = &selected_brushes; + selected_brushes.next = selected_brushes.prev = b; + + Brush_Build( b ); + } + } + else + { + Sys_Printf( "WARNING: plugin lookup failed while creating a plugin entitiy in Entity_Create\n" ); + return NULL; + } + } + else if (c->fixedsize) + { + // + // just use the selection for positioning + // + b = selected_brushes.next; + for (i=0 ; i<3 ; i++) + e->origin[i] = b->mins[i] - c->mins[i]; + + // create a custom brush + VectorAdd (c->mins, e->origin, mins); + VectorAdd (c->maxs, e->origin, maxs); + + b = Brush_Create (mins, maxs, &c->texdef); + + Entity_LinkBrush (e, b); + + // delete the current selection + Select_Delete (); + + // select the new brush + b->next = b->prev = &selected_brushes; + selected_brushes.next = selected_brushes.prev = b; + + Brush_Build( b ); + } + else + { + // + // change the selected brushes over to the new entity + // + for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next) + { + Entity_UnlinkBrush (b); + Entity_LinkBrush (e, b); + Brush_Build( b ); // so the key brush gets a name + } + } + + Sys_UpdateWindows (W_ALL); + return e; +} + + +/* +=========== +Entity_LinkBrush +=========== +*/ +void Entity_LinkBrush (entity_t *e, brush_t *b) +{ + if (b->oprev || b->onext) + Error ("Entity_LinkBrush: Allready linked"); + b->owner = e; + + b->onext = e->brushes.onext; + b->oprev = &e->brushes; + e->brushes.onext->oprev = b; + e->brushes.onext = b; +} + +/* +=========== +Entity_UnlinkBrush +=========== +*/ +void Entity_UnlinkBrush (brush_t *b) +{ + //if (!b->owner || !b->onext || !b->oprev) + if (!b->onext || !b->oprev) + Error ("Entity_UnlinkBrush: Not currently linked"); + b->onext->oprev = b->oprev; + b->oprev->onext = b->onext; + b->onext = b->oprev = NULL; + b->owner = NULL; +} + + +/* +=========== +Entity_Clone +=========== +*/ +entity_t *Entity_Clone (entity_t *e) +{ + entity_t *n; + epair_t *ep, *np; + + n = (entity_t*)qmalloc(sizeof(*n)); + n->entityId = g_entityId++; + n->brushes.onext = n->brushes.oprev = &n->brushes; + n->eclass = e->eclass; + + // add the entity to the entity list + Entity_AddToList(n, &entities); + + for (ep = e->epairs ; ep ; ep=ep->next) + { + np = (epair_t*)qmalloc(sizeof(*np)); + np->key = copystring(ep->key); + np->value = copystring(ep->value); + np->next = n->epairs; + n->epairs = np; + } + return n; +} + +int GetUniqueTargetId(int iHint) +{ + int iMin, iMax, i; + BOOL fFound; + entity_t *pe; + + fFound = FALSE; + pe = entities.next; + iMin = 0; + iMax = 0; + + for (; pe != NULL && pe != &entities ; pe = pe->next) + { + i = IntForKey(pe, "target"); + if (i) + { + iMin = min(i, iMin); + iMax = max(i, iMax); + if (i == iHint) + fFound = TRUE; + } + } + + if (fFound) + return iMax + 1; + else + return iHint; +} + +entity_t *FindEntity(char *pszKey, char *pszValue) +{ + entity_t *pe; + + pe = entities.next; + + for (; pe != NULL && pe != &entities ; pe = pe->next) + { + if (!strcmp(ValueForKey(pe, pszKey), pszValue)) + return pe; + } + + return NULL; +} + +entity_t *FindEntityInt(char *pszKey, int iValue) +{ + entity_t *pe; + + pe = entities.next; + + for (; pe != NULL && pe != &entities ; pe = pe->next) + { + if (IntForKey(pe, pszKey) == iValue) + return pe; + } + + return NULL; +} -- cgit v1.2.3