Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

ENTITY.CPP (19015B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 #include "stdafx.h"
     23 #include "qe3.h"
     24 
     25 //
     26 int g_entityId = 1;
     27 
     28 char *ValueForKey ( epair_t *&e, const char *key)
     29 {
     30   epair_t *ep;
     31   for (ep=e ; ep ; ep=ep->next)
     32   {
     33 		if (!strcmp (ep->key, key) )
     34     {
     35       return ep->value;
     36     }
     37   }
     38   return "";
     39 }
     40 
     41 
     42 char *ValueForKey (entity_t *ent, const char *key)
     43 {
     44   return ValueForKey(ent->epairs, key);
     45 }
     46 
     47 void TrackMD3Angles(entity_t *e, const char *key, const char *value)
     48 {
     49   if (strcmpi(key, "angle") != 0)
     50   {
     51     return;
     52   }
     53 
     54   if (e->eclass->fixedsize && e->eclass->nShowFlags & ECLASS_MISCMODEL)
     55   {
     56     float a = FloatForKey (e, "angle");
     57     float b = atof(value);
     58     if (a != b)
     59     {
     60       vec3_t vAngle;
     61       vAngle[0] = vAngle[1] = 0;
     62       vAngle[2] = -a;
     63       Brush_Rotate(e->brushes.onext, vAngle, e->origin, true);
     64       vAngle[2] = b;
     65       Brush_Rotate(e->brushes.onext, vAngle, e->origin, true);
     66     }
     67   }
     68 }
     69 
     70 void 	SetKeyValue (epair_t *&e, const char *key, const char *value)
     71 {
     72 	epair_t	*ep;
     73   for (ep=e ; ep ; ep=ep->next)
     74   {
     75 		if (!strcmp (ep->key, key) )
     76 		{
     77 			free (ep->value);
     78 			ep->value = (char*)qmalloc(strlen(value)+1);
     79 			strcpy (ep->value, value);
     80 			return;
     81 		}
     82   }
     83 	ep = (epair_t*)qmalloc (sizeof(*ep));
     84 	ep->next = e;
     85 	e = ep;
     86 	ep->key = (char*)qmalloc(strlen(key)+1);
     87 	strcpy (ep->key, key);
     88 	ep->value = (char*)qmalloc(strlen(value)+1);
     89 	strcpy (ep->value, value);
     90 
     91 }
     92 
     93 
     94 void 	SetKeyValue (entity_t *ent, const char *key, const char *value)
     95 {
     96 
     97 	if (ent == NULL)
     98 		return;
     99 
    100 	if (!key || !key[0])
    101 		return;
    102 
    103   TrackMD3Angles(ent, key, value);
    104 
    105   SetKeyValue(ent->epairs, key, value);
    106 
    107 }
    108 
    109 void 	DeleteKey (epair_t *&e, const char *key)
    110 {
    111 	epair_t	**ep, *next;
    112 	
    113 	ep = &e;
    114 	while (*ep)
    115 	{
    116 		next = *ep;
    117 		if ( !strcmp (next->key, key) )
    118 		{
    119 			*ep = next->next;
    120 			free(next->key);
    121 			free(next->value);
    122 			free(next);
    123 			return;
    124 		}
    125 		ep = &next->next;
    126 	}
    127 }
    128 
    129 
    130 void 	DeleteKey (entity_t *ent, const char *key)
    131 {
    132   DeleteKey(ent->epairs, key);
    133 }
    134 
    135 
    136 
    137 
    138 float	FloatForKey (entity_t *ent, const char *key)
    139 {
    140 	char	*k;
    141 	
    142 	k = ValueForKey (ent, key);
    143 	return atof(k);
    144 }
    145 
    146 int IntForKey (entity_t *ent, const char *key)
    147 {
    148 	char	*k;
    149 	
    150 	k = ValueForKey (ent, key);
    151 	return atoi(k);
    152 }
    153 
    154 void 	GetVectorForKey (entity_t *ent, const char *key, vec3_t vec)
    155 {
    156 	char	*k;
    157 	
    158 	k = ValueForKey (ent, key);
    159 	sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
    160 }
    161 
    162 /*
    163 ===============
    164 Entity_FreeEpairs
    165 
    166 Frees the entity epairs.
    167 ===============
    168 */
    169 void Entity_FreeEpairs(entity_t *e)
    170 {
    171 	epair_t	*ep, *next;
    172 
    173 	for (ep = e->epairs; ep; ep = next)
    174 	{
    175 		next = ep->next;
    176 		free (ep->key);
    177 		free (ep->value);
    178 		free (ep);
    179 	}
    180 	e->epairs = NULL;
    181 }
    182 
    183 /*
    184 ===========
    185 Entity_AddToList
    186 ===========
    187 */
    188 void Entity_AddToList(entity_t *e, entity_t *list)
    189 {
    190 	if (e->next || e->prev)
    191 		Error ("Entity_AddToList: allready linked");
    192 	e->next = list->next;
    193 	list->next->prev = e;
    194 	list->next = e;
    195 	e->prev = list;
    196 }
    197 
    198 /*
    199 ===========
    200 Entity_RemoveFromList
    201 ===========
    202 */
    203 void Entity_RemoveFromList (entity_t *e)
    204 {
    205 	if (!e->next || !e->prev)
    206 		Error ("Entity_RemoveFromList: not linked");
    207 	e->next->prev = e->prev;
    208 	e->prev->next = e->next;
    209 	e->next = e->prev = NULL;
    210 }
    211 
    212 
    213 
    214 /*
    215 ===============
    216 Entity_Free
    217 
    218 Frees the entity and any brushes is has.
    219 The entity is removed from the global entities list.
    220 ===============
    221 */
    222 void Entity_Free (entity_t *e)
    223 {
    224 	// do we have a plugin entity ?
    225 	if ( e->pPlugEnt )
    226 	{
    227 		e->pPlugEnt->DecRef();
    228 		e->pPlugEnt = NULL;
    229 	}
    230 
    231 	while (e->brushes.onext != &e->brushes)
    232 		Brush_Free (e->brushes.onext);
    233 
    234 	if (e->next)
    235 	{
    236 		e->next->prev = e->prev;
    237 		e->prev->next = e->next;
    238 	}
    239 
    240 	Entity_FreeEpairs(e);
    241 
    242 	free (e);
    243 }
    244 
    245 /*
    246 =================
    247 Entity_MemorySize
    248 =================
    249 */
    250 int Entity_MemorySize(entity_t *e)
    251 {
    252 	epair_t	*ep;
    253 	int size = 0;
    254 
    255 	for (ep = e->epairs; ep; ep = ep->next)
    256 	{
    257 		size += _msize(ep->key);
    258 		size += _msize(ep->value);
    259 		size += _msize(ep);
    260 	}
    261 	size += _msize(e);
    262 	return size;
    263 }
    264 
    265 /*
    266 =================
    267 ParseEpair
    268 =================
    269 */
    270 epair_t *ParseEpair (void)
    271 {
    272 	epair_t	*e;
    273 	
    274 	e = (epair_t*)qmalloc (sizeof(*e));
    275 	
    276 	e->key = (char*)qmalloc(strlen(token)+1);
    277 	strcpy (e->key, token);
    278 
    279 	GetToken (false);
    280 	e->value = (char*)qmalloc(strlen(token)+1);
    281 	strcpy (e->value, token);
    282 
    283 	return e;
    284 }
    285 
    286 /*
    287 ================
    288 Entity_Parse
    289 
    290 If onlypairs is set, the classname info will not
    291 be looked up, and the entity will not be added
    292 to the global list.  Used for parsing the project.
    293 ================
    294 */
    295 entity_t	*Entity_Parse (qboolean onlypairs, brush_t* pList)
    296 {
    297 	entity_t	*ent;
    298 	eclass_t	*e;
    299 	brush_t		*b;
    300 	vec3_t		mins, maxs;
    301 	epair_t		*ep;
    302 	qboolean	has_brushes;
    303 	
    304 	if (!GetToken (true))
    305 		return NULL;
    306 	
    307 	if (strcmp (token, "{") )
    308 		Error ("ParseEntity: { not found");
    309 	
    310 	ent = (entity_t*)qmalloc (sizeof(*ent));
    311 	ent->entityId = g_entityId++;
    312 	ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
    313 
    314   int n = 0;
    315 	do
    316 	{
    317 		if (!GetToken (true))
    318 		{
    319 			Warning ("ParseEntity: EOF without closing brace");
    320 			return NULL;
    321 		}
    322 		if (!strcmp (token, "}") )
    323 			break;
    324 		if (!strcmp (token, "{") )
    325 		{
    326 			b = Brush_Parse ();
    327 			if (b != NULL)
    328 			{
    329 				b->owner = ent;
    330 				// add to the end of the entity chain
    331 				b->onext = &ent->brushes;
    332 				b->oprev = ent->brushes.oprev;
    333 				ent->brushes.oprev->onext = b;
    334 				ent->brushes.oprev = b;
    335 			}
    336 			else
    337 			{
    338 				break;
    339 			}
    340 		}
    341 		else
    342 		{
    343 			ep = ParseEpair ();
    344 			ep->next = ent->epairs;
    345 			ent->epairs = ep;
    346 		}
    347 	} while (1);
    348 	
    349   // group info entity?
    350   if (strcmp(ValueForKey (ent, "classname"), "group_info") == 0)
    351     return ent;
    352 
    353 	if (onlypairs)
    354 		return ent;
    355 	
    356 	if (ent->brushes.onext == &ent->brushes)
    357 		has_brushes = false;
    358 	else
    359 		has_brushes = true;
    360 	
    361 	GetVectorForKey (ent, "origin", ent->origin);
    362 	
    363 	e = Eclass_ForName (ValueForKey (ent, "classname"), has_brushes);
    364 	ent->eclass = e;
    365 	if ( e->nShowFlags & ECLASS_PLUGINENTITY )
    366 	{
    367 		// locate the plugin
    368 		CPlugIn * pPlug = g_pParentWnd->GetPlugInMgr().PluginForModule( e->hPlug );
    369 		if (pPlug)
    370 		{
    371 			// create the plugin entity
    372 			IPluginEntity* pPlugEnt = pPlug->CreatePluginEntity( ent );
    373 			if (pPlugEnt)
    374 			{
    375 				ent->pPlugEnt = pPlugEnt;
    376 				// the brush is used to select and move
    377 				pPlugEnt->GetBounds( mins, maxs );
    378 			}
    379 			else
    380 			{
    381 				// give it a default bounding box
    382 				SetKeyValue (ent, "model", "");
    383 				mins[0] = -4; mins[1] = -4; mins[2] = -4;
    384 				maxs[0] = 4; maxs[1] = 4; maxs[2] = 4;
    385 				VectorAdd( mins, ent->origin, mins );
    386 				VectorAdd( maxs, ent->origin, maxs );
    387 			}
    388 			b = Brush_Create (mins, maxs, &ent->eclass->texdef);
    389 			Entity_LinkBrush (ent, b);
    390 			Brush_Build( b, true );
    391 		}
    392 		else
    393 			Sys_Printf("WARNING: plugin lookup failed for plugin entities\n");
    394 	}
    395 	else if (e->fixedsize)
    396 	{	// fixed size entity
    397 		if (ent->brushes.onext != &ent->brushes)
    398 		{
    399 			printf ("Warning: Fixed size entity with brushes\n");
    400 #if 0
    401 			while (ent->brushes.onext != &ent->brushes)
    402 			{	// FIXME: this will free the entity and crash!
    403 				Brush_Free (b);
    404 			}
    405 #endif
    406 			ent->brushes.next = ent->brushes.prev = &ent->brushes;
    407 		}
    408 		
    409 		// create a custom brush
    410 		VectorAdd (e->mins, ent->origin, mins);
    411 		VectorAdd (e->maxs, ent->origin, maxs);
    412 		
    413 		float a = 0;
    414 		if (e->nShowFlags & ECLASS_MISCMODEL)
    415 		{
    416 			char* p = ValueForKey(ent, "model");
    417 			if (p != NULL && strlen(p) > 0)
    418 			{
    419 				vec3_t vMin, vMax;
    420 				a = FloatForKey (ent, "angle");
    421 				if (GetCachedModel(ent, p, vMin, vMax))
    422 				{
    423 					// create a custom brush
    424 					VectorAdd (ent->md3Class->mins, ent->origin, mins);
    425 					VectorAdd (ent->md3Class->maxs, ent->origin, maxs);
    426 				}
    427 			}
    428 		}
    429 		
    430 		b = Brush_Create (mins, maxs, &e->texdef);
    431 		
    432 		if (a)
    433 		{
    434 			vec3_t vAngle;
    435 			vAngle[0] = vAngle[1] = 0;
    436 			vAngle[2] = a;
    437 			Brush_Rotate(b, vAngle, ent->origin, false);
    438 		}
    439 		
    440 		
    441 		b->owner = ent;
    442 		
    443 		b->onext = ent->brushes.onext;
    444 		b->oprev = &ent->brushes;
    445 		ent->brushes.onext->oprev = b;
    446 		ent->brushes.onext = b;
    447 	}
    448 	else
    449 	{	// brush entity
    450 		if (ent->brushes.next == &ent->brushes)
    451 			printf ("Warning: Brush entity with no brushes\n");
    452 	}
    453 	
    454 	// add all the brushes to the main list
    455 	if (pList)
    456 	{
    457 		for (b=ent->brushes.onext ; b != &ent->brushes ; b=b->onext)
    458 		{
    459 			b->next = pList->next;
    460 			pList->next->prev = b;
    461 			b->prev = pList;
    462 			pList->next = b;
    463 		}
    464 	}
    465 	
    466 	return ent;
    467 }
    468 
    469 void VectorMidpoint(vec3_t va, vec3_t vb, vec3_t& out)
    470 {
    471   for (int i = 0; i < 3; i++)
    472     out[i] = va[i] + ((vb[i] - va[i]) / 2);
    473 }
    474 
    475 
    476 /*
    477 ============
    478 Entity_Write
    479 ============
    480 */
    481 void Entity_Write (entity_t *e, FILE *f, qboolean use_region)
    482 {
    483 	epair_t		*ep;
    484 	brush_t		*b;
    485 	vec3_t		origin;
    486 	char		text[128];
    487 	int			count;
    488 
    489 	// if none of the entities brushes are in the region,
    490 	// don't write the entity at all
    491 	if (use_region)
    492 	{
    493 		// in region mode, save the camera position as playerstart
    494 		if ( !strcmp(ValueForKey (e, "classname"), "info_player_start") )
    495 		{
    496 			fprintf (f, "{\n");
    497 			fprintf (f, "\"classname\" \"info_player_start\"\n");
    498 			fprintf (f, "\"origin\" \"%i %i %i\"\n", (int)g_pParentWnd->GetCamera()->Camera().origin[0],
    499 				(int)g_pParentWnd->GetCamera()->Camera().origin[1], (int)g_pParentWnd->GetCamera()->Camera().origin[2]);
    500 			fprintf (f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamera()->Camera().angles[YAW]);
    501 			fprintf (f, "}\n");
    502 			return;
    503 		}
    504 
    505 		for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
    506 			if (!Map_IsBrushFiltered(b))
    507 				break;	// got one
    508 
    509 		if (b == &e->brushes)
    510 			return;		// nothing visible
    511 	}
    512 
    513 	if ( e->eclass->nShowFlags & ECLASS_PLUGINENTITY )
    514 	{
    515 		// NOTE: the whole brush placement / origin stuff is a mess
    516 		VectorCopy( e->origin, origin );
    517 		sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
    518 		SetKeyValue (e, "origin", text);
    519 	}
    520 	// if fixedsize, calculate a new origin based on the current
    521 	// brush position
    522 	else if (e->eclass->fixedsize)
    523 	{
    524 		if (e->eclass->nShowFlags & ECLASS_MISCMODEL && e->md3Class != NULL)
    525 		{
    526 			VectorCopy(e->origin, origin);
    527 			//VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin);
    528 		}
    529 		else
    530 		{
    531 			VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin);
    532 		}
    533 		sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
    534 		SetKeyValue (e, "origin", text);
    535 	}
    536 
    537 	fprintf (f, "{\n");
    538 	for (ep = e->epairs ; ep ; ep=ep->next)
    539 		fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value);
    540 
    541 	if (!e->eclass->fixedsize)
    542 	{
    543 		count = 0;
    544 		for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
    545 		{
    546 			if (!use_region || !Map_IsBrushFiltered (b))
    547       {
    548 				fprintf (f, "// brush %i\n", count);
    549 				count++;
    550 				Brush_Write (b, f);
    551 			}
    552 		}
    553 	}
    554 	fprintf (f, "}\n");
    555 }
    556 
    557 
    558 
    559 qboolean IsBrushSelected(brush_t* bSel)
    560 {
    561 	for (brush_t* b = selected_brushes.next ;b != NULL && b != &selected_brushes; b = b->next)
    562   {
    563     if (b == bSel)
    564       return true;
    565   }
    566   return false;
    567 }
    568 
    569 //
    570 //============
    571 //Entity_WriteSelected
    572 //============
    573 //
    574 void Entity_WriteSelected(entity_t *e, FILE *f)
    575 {
    576 	epair_t		*ep;
    577 	brush_t		*b;
    578 	vec3_t		origin;
    579 	char		text[128];
    580 	int			count;
    581 
    582 	for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
    583 		if (IsBrushSelected(b))
    584 			break;	// got one
    585 
    586 	if (b == &e->brushes)
    587 		return;		// nothing selected
    588 
    589 	// if fixedsize, calculate a new origin based on the current
    590 	// brush position
    591 	if (e->eclass->fixedsize)
    592 	{
    593     if (e->eclass->nShowFlags & ECLASS_MISCMODEL && e->md3Class != NULL)
    594     {
    595       VectorCopy(e->origin, origin);
    596 		  //VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin);
    597     }
    598     else
    599     {
    600 		  VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin);
    601     }
    602     sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
    603 		SetKeyValue (e, "origin", text);
    604 	}
    605 
    606   fprintf (f, "{\n");
    607 	for (ep = e->epairs ; ep ; ep=ep->next)
    608 	  fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value);
    609 
    610   if (!e->eclass->fixedsize)
    611   {
    612 	  count = 0;
    613 	  for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
    614 	  {
    615 		  if (IsBrushSelected(b))
    616 		  {
    617 			  fprintf (f, "// brush %i\n", count);
    618 			  count++;
    619 			  Brush_Write (b, f);
    620 		  }
    621 	  }
    622   }
    623 	fprintf (f, "}\n");
    624 }
    625 
    626 
    627 //
    628 //============
    629 //Entity_WriteSelected to a CMemFile
    630 //============
    631 //
    632 void Entity_WriteSelected(entity_t *e, CMemFile* pMemFile)
    633 {
    634 	epair_t		*ep;
    635 	brush_t		*b;
    636 	vec3_t		origin;
    637 	char		text[128];
    638 	int			count;
    639 
    640 	for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
    641 		if (IsBrushSelected(b))
    642 			break;	// got one
    643 
    644 	if (b == &e->brushes)
    645 		return;		// nothing selected
    646 
    647 	// if fixedsize, calculate a new origin based on the current
    648 	// brush position
    649 	if (e->eclass->fixedsize)
    650 	{
    651     if (e->eclass->nShowFlags & ECLASS_MISCMODEL && e->md3Class != NULL)
    652     {
    653 		  //VectorSubtract (e->brushes.onext->mins, e->md3Class->mins, origin);
    654       VectorCopy(e->origin, origin);
    655     }
    656     else
    657     {
    658 		  VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin);
    659     }
    660     sprintf (text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
    661 		SetKeyValue (e, "origin", text);
    662 	}
    663 
    664   MemFile_fprintf(pMemFile, "{\n");
    665 	for (ep = e->epairs ; ep ; ep=ep->next)
    666 	  MemFile_fprintf(pMemFile, "\"%s\" \"%s\"\n", ep->key, ep->value);
    667 
    668   if (!e->eclass->fixedsize)
    669   {
    670 	  count = 0;
    671 	  for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
    672 	  {
    673 		  if (IsBrushSelected(b))
    674 		  {
    675 			  MemFile_fprintf(pMemFile, "// brush %i\n", count);
    676 			  count++;
    677 			  Brush_Write (b, pMemFile);
    678 		  }
    679 	  }
    680   }
    681 	MemFile_fprintf(pMemFile, "}\n");
    682 }
    683 
    684 
    685 
    686 
    687 /*
    688 ============
    689 Entity_Create
    690 
    691 Creates a new entity out of the selected_brushes list.
    692 If the entity class is fixed size, the brushes are only
    693 used to find a midpoint.  Otherwise, the brushes have
    694 their ownership transfered to the new entity.
    695 ============
    696 */
    697 entity_t	*Entity_Create (eclass_t *c)
    698 {
    699 	entity_t	*e;
    700 	brush_t		*b;
    701 	vec3_t		mins, maxs;
    702 	int			i;
    703 
    704 	// check to make sure the brushes are ok
    705 
    706 	for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
    707   {
    708 		if (b->owner != world_entity)
    709 		{
    710 			Sys_Printf ("Entity NOT created, brushes not all from world\n");
    711 			Sys_Beep ();
    712 			return NULL;
    713 		}
    714   }
    715 
    716 	// create it
    717 
    718 	e = (entity_t*)qmalloc(sizeof(*e));
    719 	e->entityId = g_entityId++;
    720 	e->brushes.onext = e->brushes.oprev = &e->brushes;
    721 	e->eclass = c;
    722 	SetKeyValue (e, "classname", c->name);
    723 
    724 	// add the entity to the entity list
    725   Entity_AddToList(e, &entities);
    726 
    727 	// plugin entity ?
    728 	if (c->nShowFlags & ECLASS_PLUGINENTITY)
    729 	{
    730 		// locate the plugin
    731 		CPlugIn * pPlug = g_pParentWnd->GetPlugInMgr().PluginForModule( c->hPlug );
    732 		if (pPlug)
    733 		{
    734 			//
    735 			// just use the selection for positioning
    736 			//
    737 			b = selected_brushes.next;
    738 			for (i=0 ; i<3 ; i++)
    739 				e->origin[i] = b->mins[i] - c->mins[i];
    740 
    741 			// create the plugin entity
    742 			IPluginEntity* pPlugEnt = pPlug->CreatePluginEntity( e );
    743 			
    744 			if (pPlugEnt)
    745 			{
    746 				e->pPlugEnt = pPlugEnt;
    747 				// the brush is used to select and move
    748 				pPlugEnt->GetBounds( mins, maxs );
    749 				b = Brush_Create (mins, maxs, &c->texdef);
    750 				
    751 				Entity_LinkBrush (e, b);
    752 				
    753 				// delete the current selection
    754 				Select_Delete ();
    755 				
    756 				// select the new brush
    757 				b->next = b->prev = &selected_brushes;
    758 				selected_brushes.next = selected_brushes.prev = b;
    759 				
    760 				Brush_Build( b );
    761 			}
    762 		}
    763 		else
    764 		{
    765 			Sys_Printf( "WARNING: plugin lookup failed while creating a plugin entitiy in Entity_Create\n" );
    766 			return NULL;
    767 		}
    768 	}
    769 	else if (c->fixedsize)
    770 	{
    771 		//
    772 		// just use the selection for positioning
    773 		//
    774 		b = selected_brushes.next;
    775 		for (i=0 ; i<3 ; i++)
    776 			e->origin[i] = b->mins[i] - c->mins[i];
    777 
    778 		// create a custom brush
    779 		VectorAdd (c->mins, e->origin, mins);
    780 		VectorAdd (c->maxs, e->origin, maxs);
    781 
    782 	  b = Brush_Create (mins, maxs, &c->texdef);
    783 
    784 		Entity_LinkBrush (e, b);
    785 
    786 		// delete the current selection
    787 		Select_Delete ();
    788 
    789 		// select the new brush
    790 		b->next = b->prev = &selected_brushes;
    791 		selected_brushes.next = selected_brushes.prev = b;
    792 
    793 		Brush_Build( b );
    794 	}
    795 	else
    796 	{
    797 		//
    798 		// change the selected brushes over to the new entity
    799 		//
    800 		for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
    801 		{
    802 			Entity_UnlinkBrush (b);
    803 			Entity_LinkBrush (e, b);
    804 			Brush_Build( b );	// so the key brush gets a name
    805 		}
    806 	}
    807 
    808 	Sys_UpdateWindows (W_ALL);
    809 	return e;
    810 }
    811 
    812 
    813 /*
    814 ===========
    815 Entity_LinkBrush
    816 ===========
    817 */
    818 void Entity_LinkBrush (entity_t *e, brush_t *b)
    819 {
    820 	if (b->oprev || b->onext)
    821 		Error ("Entity_LinkBrush: Allready linked");
    822 	b->owner = e;
    823 
    824 	b->onext = e->brushes.onext;
    825 	b->oprev = &e->brushes;
    826 	e->brushes.onext->oprev = b;
    827 	e->brushes.onext = b;
    828 }
    829 
    830 /*
    831 ===========
    832 Entity_UnlinkBrush
    833 ===========
    834 */
    835 void Entity_UnlinkBrush (brush_t *b)
    836 {
    837 	//if (!b->owner || !b->onext || !b->oprev)
    838 	if (!b->onext || !b->oprev)
    839 		Error ("Entity_UnlinkBrush: Not currently linked");
    840 	b->onext->oprev = b->oprev;
    841 	b->oprev->onext = b->onext;
    842 	b->onext = b->oprev = NULL;
    843 	b->owner = NULL;
    844 }
    845 
    846 
    847 /*
    848 ===========
    849 Entity_Clone
    850 ===========
    851 */
    852 entity_t	*Entity_Clone (entity_t *e)
    853 {
    854 	entity_t	*n;
    855 	epair_t		*ep, *np;
    856 
    857 	n = (entity_t*)qmalloc(sizeof(*n));
    858 	n->entityId = g_entityId++;
    859 	n->brushes.onext = n->brushes.oprev = &n->brushes;
    860 	n->eclass = e->eclass;
    861 
    862 	// add the entity to the entity list
    863 	Entity_AddToList(n, &entities);
    864 
    865 	for (ep = e->epairs ; ep ; ep=ep->next)
    866 	{
    867 		np = (epair_t*)qmalloc(sizeof(*np));
    868 		np->key = copystring(ep->key);
    869 		np->value = copystring(ep->value);
    870 		np->next = n->epairs;
    871 		n->epairs = np;
    872 	}
    873 	return n;
    874 }
    875 
    876 int GetUniqueTargetId(int iHint)
    877 {
    878 	int iMin, iMax, i;
    879 	BOOL fFound;
    880 	entity_t *pe;
    881 	
    882 	fFound = FALSE;
    883 	pe = entities.next;
    884 	iMin = 0; 
    885 	iMax = 0;
    886 	
    887 	for (; pe != NULL && pe != &entities ; pe = pe->next)
    888 	{
    889 		i = IntForKey(pe, "target");
    890 		if (i)
    891 		{
    892 			iMin = min(i, iMin);
    893 			iMax = max(i, iMax);
    894 			if (i == iHint)
    895 				fFound = TRUE;
    896 		}
    897 	}
    898 
    899 	if (fFound)
    900 		return iMax + 1;
    901 	else
    902 		return iHint;
    903 }
    904 
    905 entity_t *FindEntity(char *pszKey, char *pszValue)
    906 {
    907 	entity_t *pe;
    908 	
    909 	pe = entities.next;
    910 	
    911 	for (; pe != NULL && pe != &entities ; pe = pe->next)
    912 	{
    913 		if (!strcmp(ValueForKey(pe, pszKey), pszValue))
    914 			return pe;
    915 	}
    916 
    917 	return NULL;
    918 }
    919 
    920 entity_t *FindEntityInt(char *pszKey, int iValue)
    921 {
    922 	entity_t *pe;
    923 	
    924 	pe = entities.next;
    925 	
    926 	for (; pe != NULL && pe != &entities ; pe = pe->next)
    927 	{
    928 		if (IntForKey(pe, pszKey) == iValue)
    929 			return pe;
    930 	}
    931 
    932 	return NULL;
    933 }