1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "tools/edit_gui_common.h"
30 
31 
32 #include "qe3.h"
33 #include "../../renderer/tr_local.h"
34 #include "../../renderer/model_local.h"	// for idRenderModelMD5
35 int g_entityId = 1;
36 
37 #define CURVE_TAG "curve_"
38 
39 extern void Brush_Resize(brush_t *b, idVec3 vMin, idVec3 vMax);
40 
GetNumKeys(entity_t * ent)41 int	GetNumKeys(entity_t *ent)
42 {
43 //	int iCount = 0;
44 //	for (epair_t* ep=ent->epairs ; ep ; ep=ep->next)
45 //	{
46 //		iCount++;
47 //	}
48 
49 	int iCount = ent->epairs.GetNumKeyVals();
50 	return iCount;
51 }
52 
GetKeyString(entity_t * ent,int iIndex)53 const char *GetKeyString(entity_t *ent, int iIndex)
54 {
55 //	for (epair_t* ep=ent->epairs ; ep ; ep=ep->next)
56 //	{
57 //		if (!iIndex--)
58 //			return ep->key;
59 //	}
60 //
61 //	assert(0);
62 //	return NULL;
63 
64 	if ( iIndex < GetNumKeys(ent) )
65 	{
66 		return ent->epairs.GetKeyVal(iIndex)->GetKey().c_str();
67 	}
68 
69 	assert(0);
70 	return NULL;
71 }
72 
73 
74 /*
75  =======================================================================================================================
76  =======================================================================================================================
77  */
ValueForKey(entity_t * ent,const char * key)78 const char *ValueForKey(entity_t *ent, const char *key) {
79 	return ent->epairs.GetString(key);
80 }
81 
82 /*
83  =======================================================================================================================
84  =======================================================================================================================
85  */
TrackMD3Angles(entity_t * e,const char * key,const char * value)86 void TrackMD3Angles(entity_t *e, const char *key, const char *value) {
87 	if ( idStr::Icmp(key, "angle") != 0 ) {
88 		return;
89 	}
90 
91 	if ((e->eclass->fixedsize && e->eclass->nShowFlags & ECLASS_MISCMODEL) || EntityHasModel(e)) {
92 		float	a = FloatForKey(e, "angle");
93 		float	b = atof(value);
94 		if (a != b) {
95 			idVec3	vAngle;
96 			vAngle[0] = vAngle[1] = 0;
97 			vAngle[2] = -a;
98 			Brush_Rotate(e->brushes.onext, vAngle, e->origin, true);
99 			vAngle[2] = b;
100 			Brush_Rotate(e->brushes.onext, vAngle, e->origin, true);
101 		}
102 	}
103 }
104 
105 /*
106  =======================================================================================================================
107  =======================================================================================================================
108  */
SetKeyValue(entity_t * ent,const char * key,const char * value,bool trackAngles)109 void SetKeyValue(entity_t *ent, const char *key, const char *value, bool trackAngles) {
110 	if (ent == NULL) {
111 		return;
112 	}
113 
114 	if (!key || !key[0]) {
115 		return;
116 	}
117 
118 	if (trackAngles) {
119 		TrackMD3Angles(ent, key, value);
120 	}
121 
122 	ent->epairs.Set(key, value);
123 	GetVectorForKey(ent, "origin", ent->origin);
124 
125 	// update sound in case this key was relevent
126 	Entity_UpdateSoundEmitter( ent );
127 }
128 
129 /*
130  =======================================================================================================================
131  =======================================================================================================================
132  */
SetKeyVec3(entity_t * ent,const char * key,idVec3 v)133 void SetKeyVec3(entity_t *ent, const char *key, idVec3 v) {
134 	if (ent == NULL) {
135 		return;
136 	}
137 
138 	if (!key || !key[0]) {
139 		return;
140 	}
141 
142 	idStr str;
143 	sprintf(str, "%g %g %g", v.x, v.y, v.z);
144 	ent->epairs.Set(key, str);
145 	GetVectorForKey(ent, "origin", ent->origin);
146 }
147 
148 /*
149  =======================================================================================================================
150  =======================================================================================================================
151  */
SetKeyMat3(entity_t * ent,const char * key,idMat3 m)152 void SetKeyMat3(entity_t *ent, const char *key, idMat3 m) {
153 	if (ent == NULL) {
154 		return;
155 	}
156 
157 	if (!key || !key[0]) {
158 		return;
159 	}
160 
161 	idStr str;
162 
163 	sprintf(str, "%g %g %g %g %g %g %g %g %g",m[0][0],m[0][1],m[0][2],m[1][0],m[1][1],m[1][2],m[2][0],m[2][1],m[2][2]);
164 
165 	ent->epairs.Set(key, str);
166 	GetVectorForKey(ent, "origin", ent->origin);
167 }
168 
169 
170 
171 /*
172  =======================================================================================================================
173  =======================================================================================================================
174  */
DeleteKey(entity_t * ent,const char * key)175 void DeleteKey(entity_t *ent, const char *key) {
176 	ent->epairs.Delete(key);
177 	if (stricmp(key, "rotation") == 0) {
178 		ent->rotation.Identity();
179 	}
180 }
181 
182 /*
183  =======================================================================================================================
184  =======================================================================================================================
185  */
FloatForKey(entity_t * ent,const char * key)186 float FloatForKey(entity_t *ent, const char *key) {
187 	const char	*k;
188 
189 	k = ValueForKey(ent, key);
190 	if (k && *k) {
191 		return atof(k);
192 	}
193 
194 	return 0.0;
195 }
196 
197 /*
198  =======================================================================================================================
199  =======================================================================================================================
200  */
IntForKey(entity_t * ent,const char * key)201 int IntForKey(entity_t *ent, const char *key) {
202 	const char	*k;
203 
204 	k = ValueForKey(ent, key);
205 	return atoi(k);
206 }
207 
208 /*
209  =======================================================================================================================
210  =======================================================================================================================
211  */
GetVectorForKey(entity_t * ent,const char * key,idVec3 & vec)212 bool GetVectorForKey(entity_t *ent, const char *key, idVec3 &vec) {
213 	const char	*k;
214 	k = ValueForKey(ent, key);
215 	if (k && strlen(k) > 0) {
216 		sscanf(k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
217 		return true;
218 	}
219 	else {
220 		vec[0] = vec[1] = vec[2] = 0;
221 	}
222 
223 	return false;
224 }
225 
226 /*
227  =======================================================================================================================
228  =======================================================================================================================
229  */
GetVector4ForKey(entity_t * ent,const char * key,idVec4 & vec)230 bool GetVector4ForKey(entity_t *ent, const char *key, idVec4 &vec) {
231 	const char	*k;
232 	k = ValueForKey(ent, key);
233 	if (k && strlen(k) > 0) {
234 		sscanf(k, "%f %f %f %f", &vec[0], &vec[1], &vec[2], &vec[3]);
235 		return true;
236 	}
237 	else {
238 		vec[0] = vec[1] = vec[2] = vec[3] = 0;
239 	}
240 
241 	return false;
242 }
243 
244 /*
245  =======================================================================================================================
246  =======================================================================================================================
247  */
GetFloatForKey(entity_t * ent,const char * key,float * f)248 bool GetFloatForKey(entity_t *ent, const char *key, float *f) {
249 	const char	*k;
250 	k = ValueForKey(ent, key);
251 	if (k && strlen(k) > 0) {
252 		*f = atof(k);
253 		return true;
254 	}
255 
256 	*f = 0;
257 	return false;
258 }
259 
260 /*
261  =======================================================================================================================
262  =======================================================================================================================
263  */
GetMatrixForKey(entity_t * ent,const char * key,idMat3 & mat)264 bool GetMatrixForKey(entity_t *ent, const char *key, idMat3 &mat) {
265 	const char	*k;
266 	k = ValueForKey(ent, key);
267 	if (k && strlen(k) > 0) {
268 		sscanf
269 		(
270 			k,
271 			"%f %f %f %f %f %f %f %f %f ",
272 			&mat[0][0],
273 			&mat[0][1],
274 			&mat[0][2],
275 			&mat[1][0],
276 			&mat[1][1],
277 			&mat[1][2],
278 			&mat[2][0],
279 			&mat[2][1],
280 			&mat[2][2]
281 		);
282 		return true;
283 	}
284 	else {
285 		mat.Identity();
286 	}
287 
288 	return false;
289 }
290 
291 /*
292  =======================================================================================================================
293 	Entity_FreeEpairs Frees the entity epairs.
294  =======================================================================================================================
295  */
Entity_FreeEpairs(entity_t * e)296 void Entity_FreeEpairs(entity_t *e) {
297 	e->epairs.Clear();
298 }
299 
300 /*
301  =======================================================================================================================
302 	Entity_AddToList
303  =======================================================================================================================
304  */
Entity_AddToList(entity_t * e,entity_t * list)305 void Entity_AddToList(entity_t *e, entity_t *list) {
306 	if (e->next || e->prev) {
307 		Error("Entity_AddToList: already linked");
308 	}
309 
310 	e->next = list->next;
311 	list->next->prev = e;
312 	list->next = e;
313 	e->prev = list;
314 }
315 
316 /*
317  =======================================================================================================================
318 	Entity_RemoveFromList
319  =======================================================================================================================
320  */
Entity_RemoveFromList(entity_t * e)321 void Entity_RemoveFromList(entity_t *e) {
322 	if ( !e->next || !e->prev ) {
323 		Error("Entity_RemoveFromList: not linked");
324 	}
325 
326 	e->next->prev = e->prev;
327 	e->prev->next = e->next;
328 	e->next = e->prev = NULL;
329 }
330 
331 /*
332  =======================================================================================================================
333 	Entity_Free Frees the entity and any brushes is has. The entity is removed from the global entities list.
334  =======================================================================================================================
335  */
Entity_Free(entity_t * e)336 void Entity_Free( entity_t *e ) {
337 
338 	while ( e->brushes.onext != &e->brushes ) {
339 		Brush_Free(e->brushes.onext);
340 	}
341 
342 	if ( e->next ) {
343 		e->next->prev = e->prev;
344 		e->prev->next = e->next;
345 	}
346 
347 	Entity_FreeEpairs( e );
348 
349 	delete e;
350 }
351 
352 /*
353  =======================================================================================================================
354 	Entity_MemorySize
355  =======================================================================================================================
356  */
357 
Entity_MemorySize(entity_t * e)358 int Entity_MemorySize( entity_t *e )
359 {
360 	brush_t		*b;
361 	int			size;
362 
363 	size = sizeof( entity_t ) + e->epairs.Size();
364 	for( b = e->brushes.onext; b != &e->brushes; b = b->onext )
365 	{
366 		size += Brush_MemorySize( b );
367 }
368 	return( size );
369 }
370 
371 /*
372  =======================================================================================================================
373 	ParseEpair
374  =======================================================================================================================
375  */
376 
377 struct EpairFixup {
378 	const char *name;
379 	int type;
380 };
381 
382 
383 const EpairFixup FloatFixups[] = {
384 	{ "origin", 1 },
385 	{ "rotation", 2 },
386 	{ "_color", 1 },
387 	{ "falloff", 0 },
388 	{ "light", 0 },
389 	{ "light_target", 1 },
390 	{ "light_up", 1 },
391 	{ "light_right", 1 },
392 	{ "light_start", 1 },
393 	{ "light_center", 1 },
394 	{ "light_end", 1 },
395 	{ "light_radius", 1 },
396 	{ "light_origin", 1 }
397 };
398 
399 const int FixupCount = sizeof(FloatFixups) / sizeof(EpairFixup);
400 
FixFloats(idDict * dict)401 void FixFloats(idDict *dict) {
402 	int count = dict->GetNumKeyVals();
403 	for (int i = 0; i < count; i++) {
404 		const idKeyValue *kv = dict->GetKeyVal(i);
405 		for (int j = 0; j < FixupCount; j++) {
406 			if (kv->GetKey().Icmp(FloatFixups[j].name) == 0) {
407 				idStr val;
408 				if (FloatFixups[j].type == 1) {
409 					idVec3 v;
410 					sscanf(kv->GetValue().c_str(), "%f %f %f", &v.x, &v.y, &v.z);
411 					sprintf(val, "%g %g %g", v.x, v.y, v.z);
412 				} else if (FloatFixups[j].type == 2) {
413 					idMat3 mat;
414 					sscanf(kv->GetValue().c_str(),	"%f %f %f %f %f %f %f %f %f ",&mat[0][0],&mat[0][1],&mat[0][2],&mat[1][0],&mat[1][1],&mat[1][2],&mat[2][0],&mat[2][1],&mat[2][2]);
415 					sprintf(val, "%g %g %g %g %g %g %g %g %g",mat[0][0],mat[0][1],mat[0][2],mat[1][0],mat[1][1],mat[1][2],mat[2][0],mat[2][1],mat[2][2]);
416 				} else {
417 					float f = atof(kv->GetValue().c_str());
418 					sprintf(val, "%g", f);
419 				}
420 				dict->Set(kv->GetKey(), val);
421 				break;
422 			}
423 		}
424 	}
425 }
426 
ParseEpair(idDict * dict)427 void ParseEpair(idDict *dict) {
428 	idStr key = token;
429 	GetToken(false);
430 	idStr val = token;
431 
432 	if (key.Length() > 0) {
433 		dict->Set(key, val);
434 	}
435 }
436 
437 /*
438  =======================================================================================================================
439  =======================================================================================================================
440  */
EntityHasModel(entity_t * ent)441 bool EntityHasModel(entity_t *ent) {
442 	if (ent) {
443 		const char	*model = ValueForKey(ent, "model");
444 		const char	*name = ValueForKey(ent, "name");
445 		if (model && *model) {
446 			if ( idStr::Icmp(model, name) ) {
447 				return true;
448 			}
449 		}
450 	}
451 
452 	return false;
453 }
454 
455 /*
456  =======================================================================================================================
457  =======================================================================================================================
458  */
Entity_New()459 entity_t *Entity_New() {
460 	entity_t *ent = new entity_t;
461 
462 	ent->prev = ent->next = NULL;
463 	ent->brushes.prev = ent->brushes.next = NULL;
464 	ent->brushes.oprev = ent->brushes.onext = NULL;
465 	ent->brushes.owner = NULL;
466 	ent->undoId = 0;
467 	ent->redoId = 0;
468 	ent->entityId = g_entityId++;
469 	ent->origin.Zero();
470 	ent->eclass = NULL;
471 	ent->md3Class = NULL;
472 	ent->lightOrigin.Zero();
473 	ent->lightRotation.Identity();
474 	ent->trackLightOrigin = false;
475 	ent->rotation.Identity();
476 	ent->lightDef = -1;
477 	ent->modelDef = -1;
478 	ent->soundEmitter = NULL;
479 	ent->curve = NULL;
480 	return ent;
481 }
482 
Entity_UpdateCurveData(entity_t * ent)483 void Entity_UpdateCurveData( entity_t *ent ) {
484 
485 	if ( ent == NULL || ent->curve == NULL ) {
486 		return;
487 	}
488 
489 	const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
490 	if ( kv == NULL ) {
491 		if ( ent->curve ) {
492 			delete ent->curve;
493 			ent->curve = NULL;
494 			if ( g_qeglobals.d_select_mode == sel_editpoint ) {
495 				g_qeglobals.d_select_mode = sel_brush;
496 			}
497 		}
498 		return;
499 	}
500 
501 	int c = ent->curve->GetNumValues();
502 	idStr str = va( "%i ( ", c );
503 	idVec3 v;
504 	for ( int i = 0; i < c; i++ ) {
505 		v = ent->curve->GetValue( i );
506 		str += " ";
507 		str += v.ToString();
508 		str += " ";
509 	}
510 	str += " )";
511 
512 	ent->epairs.Set( kv->GetKey(), str );
513 
514 }
515 
Entity_MakeCurve(entity_t * ent)516 idCurve<idVec3> *Entity_MakeCurve( entity_t *ent ) {
517 	const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
518 	if ( kv ) {
519 		idStr str = kv->GetKey().Right( kv->GetKey().Length() - strlen( CURVE_TAG ) );
520 		if ( str.Icmp( "CatmullRomSpline" ) == 0 ) {
521 			return new idCurve_CatmullRomSpline<idVec3>();
522 		} else if ( str.Icmp( "Nurbs" ) == 0 ) {
523 			return new idCurve_NURBS<idVec3>();
524 		}
525 	}
526 	return NULL;
527 }
528 
Entity_SetCurveData(entity_t * ent)529 void Entity_SetCurveData( entity_t *ent ) {
530 
531 	ent->curve = Entity_MakeCurve( ent );
532 	const idKeyValue *kv = ent->epairs.MatchPrefix( CURVE_TAG );
533 	if ( kv && ent->curve ) {
534 		idLexer lex;
535 		lex.LoadMemory( kv->GetValue(), kv->GetValue().Length(), "_curve" );
536 		int numPoints = lex.ParseInt();
537 		if ( numPoints > 0 ) {
538 			float *fp = new float[numPoints * 3];
539 			lex.Parse1DMatrix( numPoints * 3, fp );
540 			int time = 0;
541 			for ( int i = 0; i < numPoints * 3; i += 3 ) {
542 				idVec3 v;
543 				v.x = fp[i];
544 				v.y = fp[i+1];
545 				v.z = fp[i+2];
546 				ent->curve->AddValue( time, v );
547 				time += 100;
548 			}
549 			delete []fp;
550 		}
551 	}
552 
553 }
554 
Entity_PostParse(entity_t * ent,brush_t * pList)555 entity_t *Entity_PostParse(entity_t *ent, brush_t *pList) {
556 	bool		has_brushes;
557 	eclass_t	*e;
558 	brush_t		*b;
559 	idVec3		mins, maxs, zero;
560 	idBounds bo;
561 
562 	zero.Zero();
563 
564 	Entity_SetCurveData( ent );
565 
566 	if (ent->brushes.onext == &ent->brushes) {
567 		has_brushes = false;
568 	}
569 	else {
570 		has_brushes = true;
571 	}
572 
573 	bool needsOrigin = !GetVectorForKey(ent, "origin", ent->origin);
574 	const char	*pModel = ValueForKey(ent, "model");
575 
576 	const char *cp = ValueForKey(ent, "classname");
577 
578 	if (strlen(cp)) {
579 		e = Eclass_ForName(cp, has_brushes);
580 	} else {
581 		const char *cp2 = ValueForKey(ent, "name");
582 		if (strlen(cp2)) {
583 			char buff[1024];
584 			strcpy(buff, cp2);
585 			int len = strlen(buff);
586 			while ((isdigit(buff[len-1]) || buff[len-1] == '_') && len > 0) {
587 				buff[len-1] = '\0';
588 				len--;
589 			}
590 			e = Eclass_ForName(buff, has_brushes);
591 			SetKeyValue(ent, "classname", buff, false);
592 		} else {
593 			e = Eclass_ForName("", has_brushes);
594 		}
595 	}
596 
597 	idStr str;
598 
599 	if (e->defArgs.GetString("model", "", str) && e->entityModel == NULL) {
600 		e->entityModel = gameEdit->ANIM_GetModelFromEntityDef( &e->defArgs );
601 	}
602 
603 	ent->eclass = e;
604 
605 	bool hasModel = EntityHasModel(ent);
606 
607 	if (hasModel) {
608 		ent->eclass->defArgs.GetString("model", "", str);
609 		if (str.Length()) {
610 			hasModel = false;
611 			ent->epairs.Delete("model");
612 		}
613 	}
614 
615 	if (e->nShowFlags & ECLASS_WORLDSPAWN) {
616 		ent->origin.Zero();
617 		needsOrigin = false;
618 		ent->epairs.Delete( "model" );
619 	} else if (e->nShowFlags & ECLASS_LIGHT) {
620 		if (GetVectorForKey(ent, "light_origin", ent->lightOrigin)) {
621 			GetMatrixForKey(ent, "light_rotation", ent->lightRotation);
622 			ent->trackLightOrigin = true;
623 		} else if (hasModel) {
624 			SetKeyValue(ent, "light_origin", ValueForKey(ent, "origin"));
625 			ent->lightOrigin = ent->origin;
626 			if (GetMatrixForKey(ent, "rotation", ent->lightRotation)) {
627 				SetKeyValue(ent, "light_rotation", ValueForKey(ent, "rotation"));
628 			}
629 			ent->trackLightOrigin = true;
630 		}
631 	} else if ( e->nShowFlags & ECLASS_ENV ) {
632 		// need to create an origin from the bones here
633 		idVec3 org;
634 		idAngles ang;
635 		bo.Clear();
636 		bool hasBody = false;
637 		const idKeyValue *arg = ent->epairs.MatchPrefix( "body ", NULL );
638 		while ( arg ) {
639 			sscanf( arg->GetValue(), "%f %f %f %f %f %f", &org.x, &org.y, &org.z, &ang.pitch, &ang.yaw, &ang.roll );
640 			bo.AddPoint( org );
641 			arg = ent->epairs.MatchPrefix( "body ", arg );
642 			hasBody = true;
643 		}
644 		if (hasBody) {
645 			ent->origin = bo.GetCenter();
646 		}
647 	}
648 
649 	if (e->fixedsize || hasModel) {			// fixed size entity
650 		if (ent->brushes.onext != &ent->brushes) {
651 			for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
652 				b->entityModel = true;
653 			}
654 		}
655 
656 		if (hasModel) {
657 			// model entity
658 			idRenderModel *modelHandle = renderModelManager->FindModel( pModel );
659 
660 			if ( dynamic_cast<idRenderModelPrt*>( modelHandle ) || dynamic_cast<idRenderModelLiquid*>( modelHandle ) ) {
661 				bo.Zero();
662 				bo.ExpandSelf( 12.0f );
663 			} else {
664 				bo = modelHandle->Bounds( NULL );
665 			}
666 
667 			VectorCopy(bo[0], mins);
668 			VectorCopy(bo[1], maxs);
669 			for (int i = 0; i < 3; i++) {
670 				if (mins[i] == maxs[i]) {
671 					mins[i]--;
672 					maxs[i]++;
673 				}
674 			}
675 			VectorAdd(mins, ent->origin, mins);
676 			VectorAdd(maxs, ent->origin, maxs);
677 			b = Brush_Create(mins, maxs, &e->texdef);
678 			b->modelHandle = modelHandle;
679 
680 			float		yaw = 0;
681 			bool		convertAngles = GetFloatForKey(ent, "angle", &yaw);
682 			extern void Brush_Rotate(brush_t *b, idMat3 matrix, idVec3 origin, bool bBuild);
683 			extern void Brush_Rotate(brush_t *b, idVec3 rot, idVec3 origin, bool bBuild);
684 
685 			if (convertAngles) {
686 				idVec3	rot(0, 0, yaw);
687 				Brush_Rotate(b, rot, ent->origin, false);
688 			}
689 
690 			if (GetMatrixForKey(ent, "rotation", ent->rotation)) {
691 				idBounds bo2;
692 				bo2.FromTransformedBounds(bo, ent->origin, ent->rotation);
693 				b->owner = ent;
694 				Brush_Resize(b, bo2[0], bo2[1]);
695 			}
696 			Entity_LinkBrush(ent, b);
697 		}
698 
699 		if (!hasModel || (ent->eclass->nShowFlags & ECLASS_LIGHT && hasModel)) {
700 			// create a custom brush
701 			if (ent->trackLightOrigin) {
702 				mins = e->mins + ent->lightOrigin;
703 				maxs = e->maxs + ent->lightOrigin;
704 			} else {
705 				mins = e->mins + ent->origin;
706 				maxs = e->maxs + ent->origin;
707 			}
708 
709 			b = Brush_Create(mins, maxs, &e->texdef);
710 			GetMatrixForKey(ent, "rotation", ent->rotation);
711 			Entity_LinkBrush(ent, b);
712 			b->trackLightOrigin = ent->trackLightOrigin;
713 			if ( e->texdef.name == NULL ) {
714 				brushprimit_texdef_t bp;
715 				texdef_t td;
716 				td.SetName( ent->eclass->defMaterial );
717 				Brush_SetTexture( b, &td, &bp, false );
718 			}
719 		}
720 	} else {	// brush entity
721 		if (ent->brushes.next == &ent->brushes) {
722 			printf("Warning: Brush entity with no brushes\n");
723 		}
724 
725 		if (!needsOrigin) {
726 			idStr cn = ValueForKey(ent, "classname");
727 			idStr name = ValueForKey(ent, "name");
728 			idStr model = ValueForKey(ent, "model");
729 			if (cn.Icmp("func_static") == 0) {
730 				if (name.Icmp(model) == 0) {
731 					needsOrigin = true;
732 				}
733 			}
734 		}
735 
736 		if (needsOrigin) {
737 			idVec3	mins, maxs, mid;
738 			int		i;
739 			char	text[32];
740 			mins[0] = mins[1] = mins[2] = 999999;
741 			maxs[0] = maxs[1] = maxs[2] = -999999;
742 
743 			// add in the origin
744 			for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
745 				Brush_Build(b, true, false, false);
746 				for (i = 0; i < 3; i++) {
747 					if (b->mins[i] < mins[i]) {
748 						mins[i] = b->mins[i];
749 					}
750 
751 					if (b->maxs[i] > maxs[i]) {
752 						maxs[i] = b->maxs[i];
753 					}
754 				}
755 			}
756 
757 			for (i = 0; i < 3; i++) {
758 				ent->origin[i] = (mins[i] + ((maxs[i] - mins[i]) / 2));
759 			}
760 
761 			sprintf(text, "%i %i %i", (int)ent->origin[0], (int)ent->origin[1], (int)ent->origin[2]);
762 			SetKeyValue(ent, "origin", text);
763 		}
764 
765 		if (!(e->nShowFlags & ECLASS_WORLDSPAWN)) {
766 			if (e->defArgs.FindKey("model") == NULL && (pModel == NULL || (pModel && strlen(pModel) == 0))) {
767 				SetKeyValue(ent, "model", ValueForKey(ent, "name"));
768 			}
769 		}
770 		else {
771 			DeleteKey(ent, "origin");
772 		}
773 	}
774 
775 	// add all the brushes to the main list
776 	if (pList) {
777 		for (b = ent->brushes.onext; b != &ent->brushes; b = b->onext) {
778 			b->next = pList->next;
779 			pList->next->prev = b;
780 			b->prev = pList;
781 			pList->next = b;
782 		}
783 	}
784 
785 	FixFloats(&ent->epairs);
786 
787 	return ent;
788 
789 }
790 
791 /*
792  =======================================================================================================================
793 	Entity_Parse If onlypairs is set, the classname info will not be looked up, and the entity will not be added to the
794 	global list. Used for parsing the project.
795  =======================================================================================================================
796  */
Entity_Parse(bool onlypairs,brush_t * pList)797 entity_t *Entity_Parse(bool onlypairs, brush_t *pList) {
798 	entity_t	*ent;
799 
800 	if (!GetToken(true)) {
801 		return NULL;
802 	}
803 
804 	if (strcmp(token, "{")) {
805 		Error("ParseEntity: { not found");
806 	}
807 
808 	ent = Entity_New();
809 	ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
810 	ent->origin.Zero();
811 
812 	int n = 0;
813 	do {
814 		if (!GetToken(true)) {
815 			Warning("ParseEntity: EOF without closing brace");
816 			return NULL;
817 		}
818 
819 		if (!strcmp(token, "}")) {
820 			break;
821 		}
822 
823 		if (!strcmp(token, "{")) {
824 			GetVectorForKey(ent, "origin", ent->origin);
825 			brush_t *b = Brush_Parse(ent->origin);
826 			if (b != NULL) {
827 				b->owner = ent;
828 
829 				// add to the end of the entity chain
830 				b->onext = &ent->brushes;
831 				b->oprev = ent->brushes.oprev;
832 				ent->brushes.oprev->onext = b;
833 				ent->brushes.oprev = b;
834 			}
835 			else {
836 				break;
837 			}
838 		}
839 		else {
840 			ParseEpair(&ent->epairs);
841 		}
842 	} while (1);
843 
844 	if (onlypairs) {
845 		return ent;
846 	}
847 
848 	return Entity_PostParse(ent, pList);
849 }
850 
851 /*
852  =======================================================================================================================
853  =======================================================================================================================
854  */
VectorMidpoint(idVec3 va,idVec3 vb,idVec3 & out)855 void VectorMidpoint(idVec3 va, idVec3 vb, idVec3 &out) {
856 	for (int i = 0; i < 3; i++) {
857 		out[i] = va[i] + ((vb[i] - va[i]) / 2);
858 	}
859 }
860 
861 /*
862  =======================================================================================================================
863 	Entity_Write
864  =======================================================================================================================
865  */
Entity_Write(entity_t * e,FILE * f,bool use_region)866 void Entity_Write(entity_t *e, FILE *f, bool use_region) {
867 	brush_t *b;
868 	idVec3	origin;
869 	char	text[128];
870 	int		count;
871 
872 	// if none of the entities brushes are in the region, don't write the entity at all
873 	if (use_region) {
874 		// in region mode, save the camera position as playerstart
875 		if (!strcmp(ValueForKey(e, "classname"), "info_player_start")) {
876 			fprintf(f, "{\n");
877 			fprintf(f, "\"classname\" \"info_player_start\"\n");
878 			fprintf
879 			(
880 				f,
881 				"\"origin\" \"%i %i %i\"\n",
882 				(int)g_pParentWnd->GetCamera()->Camera().origin[0],
883 				(int)g_pParentWnd->GetCamera()->Camera().origin[1],
884 				(int)g_pParentWnd->GetCamera()->Camera().origin[2]
885 			);
886 			fprintf(f, "\"angle\" \"%i\"\n", (int)g_pParentWnd->GetCamera()->Camera().angles[YAW]);
887 			fprintf(f, "}\n");
888 			return;
889 		}
890 
891 		for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
892 			if (!Map_IsBrushFiltered(b)) {
893 				break;	// got one
894 			}
895 		}
896 
897 		if (b == &e->brushes) {
898 			return;		// nothing visible
899 		}
900 	}
901 
902 	if (e->eclass->nShowFlags & ECLASS_PLUGINENTITY) {
903 		// NOTE: the whole brush placement / origin stuff is a mess
904 		VectorCopy(e->origin, origin);
905 		sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
906 		SetKeyValue(e, "origin", text);
907 	}
908 
909 	// if fixedsize, calculate a new origin based on the current brush position
910 	else if (e->eclass->fixedsize || EntityHasModel(e)) {
911 		if (!GetVectorForKey(e, "origin", origin)) {
912 			VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
913 			sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
914 			SetKeyValue(e, "origin", text);
915 		}
916 	}
917 
918 	fprintf(f, "{\n");
919 
920 	count = e->epairs.GetNumKeyVals();
921 	for (int j = 0; j < count; j++) {
922 		fprintf(f, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
923 	}
924 
925 	if (!EntityHasModel(e)) {
926 		count = 0;
927 		for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
928 			if (e->eclass->fixedsize && !b->entityModel) {
929 				continue;
930 			}
931 			if (!use_region || !Map_IsBrushFiltered(b)) {
932 				fprintf(f, "// brush %i\n", count);
933 				count++;
934 				Brush_Write( b, f, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
935 			}
936 		}
937 	}
938 
939 	fprintf(f, "}\n");
940 }
941 
942 /*
943  =======================================================================================================================
944  =======================================================================================================================
945  */
IsBrushSelected(brush_t * bSel)946 bool IsBrushSelected(brush_t *bSel) {
947 	for (brush_t * b = selected_brushes.next; b != NULL && b != &selected_brushes; b = b->next) {
948 		if (b == bSel) {
949 			return true;
950 		}
951 	}
952 
953 	return false;
954 }
955 
956 //
957 // =======================================================================================================================
958 //    Entity_WriteSelected
959 // =======================================================================================================================
960 //
Entity_WriteSelected(entity_t * e,FILE * f)961 void Entity_WriteSelected(entity_t *e, FILE *f) {
962 	brush_t *b;
963 	idVec3	origin;
964 	char	text[128];
965 	int		count;
966 
967 	for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
968 		if (IsBrushSelected(b)) {
969 			break;	// got one
970 		}
971 	}
972 
973 	if (b == &e->brushes) {
974 		return;		// nothing selected
975 	}
976 
977 	// if fixedsize, calculate a new origin based on the current brush position
978 	if (e->eclass->fixedsize || EntityHasModel(e)) {
979 		if (!GetVectorForKey(e, "origin", origin)) {
980 			VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
981 			sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
982 			SetKeyValue(e, "origin", text);
983 		}
984 	}
985 
986 	fprintf(f, "{\n");
987 
988 	count = e->epairs.GetNumKeyVals();
989 	for (int j = 0; j < count; j++) {
990 		fprintf(f, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
991 	}
992 
993 	if (!EntityHasModel(e)) {
994 		count = 0;
995 		for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
996 			if (e->eclass->fixedsize && !b->entityModel) {
997 				continue;
998 			}
999 			if (IsBrushSelected(b)) {
1000 				fprintf(f, "// brush %i\n", count);
1001 				count++;
1002 				Brush_Write( b, f, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
1003 			}
1004 		}
1005 	}
1006 
1007 	fprintf(f, "}\n");
1008 }
1009 
1010 //
1011 // =======================================================================================================================
1012 //    Entity_WriteSelected to a CMemFile
1013 // =======================================================================================================================
1014 //
Entity_WriteSelected(entity_t * e,CMemFile * pMemFile)1015 void Entity_WriteSelected(entity_t *e, CMemFile *pMemFile) {
1016 	brush_t *b;
1017 	idVec3	origin;
1018 	char	text[128];
1019 	int		count;
1020 
1021 	for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
1022 		if (IsBrushSelected(b)) {
1023 			break;	// got one
1024 		}
1025 	}
1026 
1027 	if (b == &e->brushes) {
1028 		return;		// nothing selected
1029 	}
1030 
1031 	// if fixedsize, calculate a new origin based on the current brush position
1032 	if (e->eclass->fixedsize || EntityHasModel(e)) {
1033 		if (!GetVectorForKey(e, "origin", origin)) {
1034 			VectorSubtract(e->brushes.onext->mins, e->eclass->mins, origin);
1035 			sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
1036 			SetKeyValue(e, "origin", text);
1037 		}
1038 	}
1039 
1040 	MemFile_fprintf(pMemFile, "{\n");
1041 
1042 	count = e->epairs.GetNumKeyVals();
1043 	for (int j = 0; j < count; j++) {
1044 		MemFile_fprintf(pMemFile, "\"%s\" \"%s\"\n", e->epairs.GetKeyVal(j)->GetKey().c_str(), e->epairs.GetKeyVal(j)->GetValue().c_str());
1045 	}
1046 
1047 	if (!EntityHasModel(e)) {
1048 		count = 0;
1049 		for (b = e->brushes.onext; b != &e->brushes; b = b->onext) {
1050 			if (e->eclass->fixedsize && !b->entityModel) {
1051 				continue;
1052 			}
1053 			if (IsBrushSelected(b)) {
1054 				MemFile_fprintf(pMemFile, "// brush %i\n", count);
1055 				count++;
1056 				Brush_Write( b, pMemFile, e->origin, ( g_PrefsDlg.m_bNewMapFormat != FALSE ) );
1057 			}
1058 		}
1059 	}
1060 
1061 	MemFile_fprintf(pMemFile, "}\n");
1062 }
1063 
1064 /*
1065  =======================================================================================================================
1066  =======================================================================================================================
1067  */
Entity_SetName(entity_t * e,const char * name)1068 void Entity_SetName(entity_t *e, const char *name) {
1069 	CString oldName = ValueForKey(e, "name");
1070 	CString oldModel = ValueForKey(e, "model");
1071 	SetKeyValue(e, "name", name);
1072 	if (oldName == oldModel) {
1073 		SetKeyValue(e, "model", name);
1074 	}
1075 }
1076 
1077 extern bool Entity_NameIsUnique(const char *name);
1078 
1079 /*
1080  =======================================================================================================================
1081  =======================================================================================================================
1082  */
Entity_Name(entity_t * e,bool force)1083 void Entity_Name(entity_t *e, bool force) {
1084 	const char	*name = ValueForKey(e, "name");
1085 
1086 	if (!force && name && name[0]) {
1087 		return;
1088 	}
1089 
1090 	if (name && name[0] && Entity_NameIsUnique(name)) {
1091 		return;
1092 	}
1093 
1094 	bool	setModel = false;
1095 	if (name[0]) {
1096 		const char	*model = ValueForKey(e, "model");
1097 		if (model[0]) {
1098 			if ( idStr::Icmp(model, name) == 0 ) {
1099 				setModel = true;
1100 			}
1101 		}
1102 	}
1103 
1104 	const char *eclass = ValueForKey(e, "classname");
1105 	if (eclass && eclass[0]) {
1106 		idStr str = cvarSystem->GetCVarString( "radiant_nameprefix" );
1107 		int id = Map_GetUniqueEntityID(str, eclass);
1108 		if (str.Length()) {
1109 			SetKeyValue(e, "name", va("%s_%s_%i", str.c_str(), eclass, id));
1110 		} else {
1111 			SetKeyValue(e, "name", va("%s_%i", eclass, id));
1112 		}
1113 		if (setModel) {
1114 			if (str.Length()) {
1115 				SetKeyValue(e, "model", va("%s_%s_%i", str.c_str(), eclass, id));
1116 			} else {
1117 				SetKeyValue(e, "model", va("%s_%i",  eclass, id));
1118 			}
1119 		}
1120 	}
1121 }
1122 
1123 /*
1124  =======================================================================================================================
1125 	Entity_Create Creates a new entity out of the selected_brushes list. If the entity class is fixed size, the brushes
1126 	are only used to find a midpoint. Otherwise, the brushes have their ownership transfered to the new entity.
1127  =======================================================================================================================
1128  */
Entity_Create(eclass_t * c,bool forceFixed)1129 entity_t *Entity_Create(eclass_t *c, bool forceFixed) {
1130 	entity_t	*e;
1131 	brush_t		*b;
1132 	idVec3		mins, maxs, origin;
1133 	char		text[32];
1134 	texdef_t td;
1135 	brushprimit_texdef_t bp;
1136 
1137 	// check to make sure the brushes are ok
1138 	for (b = selected_brushes.next; b != &selected_brushes; b = b->next) {
1139 		if (b->owner != world_entity) {
1140 			Sys_Status("Entity NOT created, brushes not all from world\n");
1141 			Sys_Beep();
1142 			return NULL;
1143 		}
1144 	}
1145 
1146 	idStr str;
1147 	if (c->defArgs.GetString("model", "", str) && c->entityModel == NULL) {
1148 		c->entityModel = gameEdit->ANIM_GetModelFromEntityDef( &c->defArgs );
1149 	}
1150 
1151 	// create it
1152 	e = Entity_New();
1153 	e->brushes.onext = e->brushes.oprev = &e->brushes;
1154 	e->eclass = c;
1155 	e->epairs.Copy(c->args);
1156 	SetKeyValue(e, "classname", c->name);
1157 	Entity_Name(e, false);
1158 
1159 	// add the entity to the entity list
1160 	Entity_AddToList(e, &entities);
1161 
1162 	if (c->fixedsize) {
1163 		//
1164 		// just use the selection for positioning b = selected_brushes.next; for (i=0 ;
1165 		// i<3 ; i++) { e->origin[i] = b->mins[i] - c->mins[i]; }
1166 		//
1167 		Select_GetMid(e->origin);
1168 		VectorCopy(e->origin, origin);
1169 
1170 		// create a custom brush
1171 		VectorAdd(c->mins, e->origin, mins);
1172 		VectorAdd(c->maxs, e->origin, maxs);
1173 
1174 		b = Brush_Create(mins, maxs, &c->texdef);
1175 
1176 		Entity_LinkBrush(e, b);
1177 
1178 		if (c->defMaterial.Length()) {
1179 			td.SetName(c->defMaterial);
1180 			Brush_SetTexture(b, &td, &bp, false);
1181 		}
1182 
1183 
1184 		// delete the current selection
1185 		Select_Delete();
1186 
1187 		// select the new brush
1188 		b->next = b->prev = &selected_brushes;
1189 		selected_brushes.next = selected_brushes.prev = b;
1190 
1191 		Brush_Build(b);
1192 	}
1193 	else {
1194 
1195 		Select_GetMid(origin);
1196 
1197 		// change the selected brushes over to the new entity
1198 		for (b = selected_brushes.next; b != &selected_brushes; b = b->next) {
1199 			Entity_UnlinkBrush(b);
1200 			Entity_LinkBrush(e, b);
1201 			Brush_Build(b); // so the key brush gets a name
1202 			if (c->defMaterial.Length()) {
1203 				td.SetName(c->defMaterial);
1204 				Brush_SetTexture(b, &td, &bp, false);
1205 			}
1206 
1207 		}
1208 
1209 		//for (int i = 0; i < 3; i++) {
1210 		//	origin[i] = vMin[i] + vMax[i] * 0.5;
1211 		//}
1212 
1213 		if (!forceFixed) {
1214 			SetKeyValue(e, "model", ValueForKey(e, "name"));
1215 		}
1216 	}
1217 
1218 	sprintf(text, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
1219 	SetKeyValue(e, "origin", text);
1220 	VectorCopy(origin, e->origin);
1221 
1222 	Sys_UpdateWindows(W_ALL);
1223 	return e;
1224 }
1225 
Brush_MakeDirty(brush_t * b)1226 void Brush_MakeDirty(brush_t *b) {
1227 	for (face_t *f = b->brush_faces; f; f = f->next) {
1228 		f->dirty = true;
1229 	}
1230 }
1231 /*
1232  =======================================================================================================================
1233 	Entity_LinkBrush
1234  =======================================================================================================================
1235  */
Entity_LinkBrush(entity_t * e,brush_t * b)1236 void Entity_LinkBrush(entity_t *e, brush_t *b) {
1237 	if (b->oprev || b->onext) {
1238 		Error("Entity_LinkBrush: Allready linked");
1239 	}
1240 
1241 	Brush_MakeDirty(b);
1242 
1243 	b->owner = e;
1244 
1245 	b->onext = e->brushes.onext;
1246 	b->oprev = &e->brushes;
1247 	e->brushes.onext->oprev = b;
1248 	e->brushes.onext = b;
1249 }
1250 
1251 /*
1252  =======================================================================================================================
1253 	Entity_UnlinkBrush
1254  =======================================================================================================================
1255  */
Entity_UnlinkBrush(brush_t * b)1256 void Entity_UnlinkBrush(brush_t *b) {
1257 	// if (!b->owner || !b->onext || !b->oprev)
1258 	if (!b->onext || !b->oprev) {
1259 		Error("Entity_UnlinkBrush: Not currently linked");
1260 	}
1261 
1262 	b->onext->oprev = b->oprev;
1263 	b->oprev->onext = b->onext;
1264 	b->onext = b->oprev = NULL;
1265 	b->owner = NULL;
1266 }
1267 
1268 /*
1269  =======================================================================================================================
1270 	Entity_Clone
1271  =======================================================================================================================
1272  */
Entity_Clone(entity_t * e)1273 entity_t *Entity_Clone(entity_t *e) {
1274 	entity_t	*n;
1275 
1276 	n = Entity_New();
1277 	n->brushes.onext = n->brushes.oprev = &n->brushes;
1278 	n->eclass = e->eclass;
1279 	n->rotation = e->rotation;
1280 	n->origin = e->origin;
1281 
1282 	// add the entity to the entity list
1283 	Entity_AddToList(n, &entities);
1284 
1285 	n->epairs  = e->epairs;
1286 
1287 	return n;
1288 }
1289 
1290 /*
1291  =======================================================================================================================
1292  =======================================================================================================================
1293  */
GetUniqueTargetId(int iHint)1294 int GetUniqueTargetId(int iHint) {
1295 	int			iMin, iMax, i;
1296 	BOOL		fFound;
1297 	entity_t	*pe;
1298 
1299 	fFound = FALSE;
1300 	pe = entities.next;
1301 	iMin = 0;
1302 	iMax = 0;
1303 
1304 	for (; pe != NULL && pe != &entities; pe = pe->next) {
1305 		i = IntForKey(pe, "target");
1306 		if (i) {
1307 			iMin = Min(i, iMin);
1308 			iMax = Max(i, iMax);
1309 			if (i == iHint) {
1310 				fFound = TRUE;
1311 			}
1312 		}
1313 	}
1314 
1315 	if (fFound) {
1316 		return iMax + 1;
1317 	}
1318 	else {
1319 		return iHint;
1320 	}
1321 }
1322 
1323 /*
1324  =======================================================================================================================
1325  =======================================================================================================================
1326  */
FindEntity(const char * pszKey,const char * pszValue)1327 entity_t *FindEntity(const char *pszKey, const char *pszValue) {
1328 	entity_t	*pe;
1329 
1330 	pe = entities.next;
1331 
1332 	for (; pe != NULL && pe != &entities; pe = pe->next) {
1333 		if (!strcmp(ValueForKey(pe, pszKey), pszValue)) {
1334 			return pe;
1335 		}
1336 	}
1337 
1338 	return NULL;
1339 }
1340 
1341 /*
1342  =======================================================================================================================
1343  =======================================================================================================================
1344  */
FindEntityInt(const char * pszKey,int iValue)1345 entity_t *FindEntityInt(const char *pszKey, int iValue) {
1346 	entity_t	*pe;
1347 
1348 	pe = entities.next;
1349 
1350 	for (; pe != NULL && pe != &entities; pe = pe->next) {
1351 		if (IntForKey(pe, pszKey) == iValue) {
1352 			return pe;
1353 		}
1354 	}
1355 
1356 	return NULL;
1357 }
1358 
1359 /*
1360 ====================
1361 Entity_UpdateSoundEmitter
1362 
1363 Deletes the soundEmitter if the entity should not emit a sound due
1364 to it not having one, being filtered away, or the sound mode being turned off.
1365 
1366 Creates or updates the soundEmitter if needed
1367 ====================
1368 */
Entity_UpdateSoundEmitter(entity_t * ent)1369 void Entity_UpdateSoundEmitter( entity_t *ent ) {
1370 	bool	playing = false;
1371 
1372 	// if an entity doesn't have any brushes at all, don't do anything
1373 	// if the brush isn't displayed (filtered or culled), don't do anything
1374 	if ( g_pParentWnd->GetCamera()->GetSoundMode()
1375 		&& ent->brushes.onext != &ent->brushes && !FilterBrush(ent->brushes.onext) ) {
1376 		// check for sounds
1377 		const char *v = ValueForKey( ent, "s_shader" );
1378 		if ( v[0] ) {
1379 			refSound_t	sound;
1380 
1381 			gameEdit->ParseSpawnArgsToRefSound( &ent->epairs, &sound );
1382 			if ( !sound.waitfortrigger ) {	// waitfortrigger will not start playing immediately
1383 				if ( !ent->soundEmitter ) {
1384 					ent->soundEmitter = g_qeglobals.sw->AllocSoundEmitter();
1385 				}
1386 				playing = true;
1387 				ent->soundEmitter->UpdateEmitter( ent->origin, 0, &sound.parms );
1388 				// always play on a single channel, so updates always override
1389 				ent->soundEmitter->StartSound( sound.shader, SCHANNEL_ONE );
1390 			}
1391 		}
1392 	}
1393 
1394 	// delete the soundEmitter if not used
1395 	if ( !playing && ent->soundEmitter ) {
1396 		ent->soundEmitter->Free( true );
1397 		ent->soundEmitter = NULL;
1398 	}
1399 
1400 }
1401