1 /*
2 ===========================================================================
3 Copyright (C) 1997-2006 Id Software, Inc.
4
5 This file is part of Quake 2 Tools source code.
6
7 Quake 2 Tools 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 2 Tools 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 Quake 2 Tools source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22
23 #include "qe3.h"
24
ValueForKey(entity_t * ent,char * key)25 char *ValueForKey (entity_t *ent, char *key)
26 {
27 epair_t *ep;
28
29 for (ep=ent->epairs ; ep ; ep=ep->next)
30 if (!strcmp (ep->key, key) )
31 return ep->value;
32 return "";
33 }
34
SetKeyValue(entity_t * ent,char * key,char * value)35 void SetKeyValue (entity_t *ent, char *key, char *value)
36 {
37 epair_t *ep;
38
39 if (ent == NULL)
40 return;
41
42 if (!key || !key[0])
43 return;
44
45 for (ep=ent->epairs ; ep ; ep=ep->next)
46 if (!strcmp (ep->key, key) )
47 {
48 free (ep->value);
49 ep->value = qmalloc(strlen(value)+1);
50 strcpy (ep->value, value);
51 return;
52 }
53 ep = qmalloc (sizeof(*ep));
54 ep->next = ent->epairs;
55 ent->epairs = ep;
56 ep->key = qmalloc(strlen(key)+1);
57 strcpy (ep->key, key);
58 ep->value = qmalloc(strlen(value)+1);
59 strcpy (ep->value, value);
60 }
61
DeleteKey(entity_t * ent,char * key)62 void DeleteKey (entity_t *ent, char *key)
63 {
64 epair_t **ep, *next;
65
66 ep = &ent->epairs;
67 while (*ep)
68 {
69 next = *ep;
70 if ( !strcmp (next->key, key) )
71 {
72 *ep = next->next;
73 free(next->key);
74 free(next->value);
75 free(next);
76 return;
77 }
78 ep = &next->next;
79 }
80 }
81
FloatForKey(entity_t * ent,char * key)82 float FloatForKey (entity_t *ent, char *key)
83 {
84 char *k;
85
86 k = ValueForKey (ent, key);
87 return atof(k);
88 }
89
IntForKey(entity_t * ent,char * key)90 int IntForKey (entity_t *ent, char *key)
91 {
92 char *k;
93
94 k = ValueForKey (ent, key);
95 return atoi(k);
96 }
97
GetVectorForKey(entity_t * ent,char * key,vec3_t vec)98 void GetVectorForKey (entity_t *ent, char *key, vec3_t vec)
99 {
100 char *k;
101
102 k = ValueForKey (ent, key);
103 sscanf (k, "%f %f %f", &vec[0], &vec[1], &vec[2]);
104 }
105
106
107 /*
108 ===============
109 Entity_Free
110
111 Frees the entity and any brushes is has.
112 The entity is removed from the global entities list.
113 ===============
114 */
Entity_Free(entity_t * e)115 void Entity_Free (entity_t *e)
116 {
117 epair_t *ep, *next;
118
119 while (e->brushes.onext != &e->brushes)
120 Brush_Free (e->brushes.onext);
121
122 if (e->next)
123 {
124 e->next->prev = e->prev;
125 e->prev->next = e->next;
126 }
127
128 for (ep = e->epairs ; ep ; ep=next)
129 {
130 next = ep->next;
131 free (ep);
132 }
133 free (e);
134 }
135
136 /*
137 =================
138 ParseEpair
139 =================
140 */
ParseEpair(void)141 epair_t *ParseEpair (void)
142 {
143 epair_t *e;
144
145 e = qmalloc (sizeof(*e));
146
147 e->key = qmalloc(strlen(token)+1);
148 strcpy (e->key, token);
149
150 GetToken (false);
151 e->value = qmalloc(strlen(token)+1);
152 strcpy (e->value, token);
153
154 return e;
155 }
156
157 /*
158 ================
159 Entity_Parse
160
161 If onlypairs is set, the classname info will not
162 be looked up, and the entity will not be added
163 to the global list. Used for parsing the project.
164 ================
165 */
Entity_Parse(qboolean onlypairs)166 entity_t *Entity_Parse (qboolean onlypairs)
167 {
168 entity_t *ent;
169 eclass_t *e;
170 brush_t *b;
171 vec3_t mins, maxs;
172 epair_t *ep;
173 qboolean has_brushes;
174
175 if (!GetToken (true))
176 return NULL;
177
178 if (strcmp (token, "{") )
179 Error ("ParseEntity: { not found");
180
181 ent = qmalloc (sizeof(*ent));
182 ent->brushes.onext = ent->brushes.oprev = &ent->brushes;
183
184 do
185 {
186 if (!GetToken (true))
187 Error ("ParseEntity: EOF without closing brace");
188 if (!strcmp (token, "}") )
189 break;
190 if (!strcmp (token, "{") )
191 {
192 b = Brush_Parse ();
193 b->owner = ent;
194
195 // add to the end of the entity chain
196 b->onext = &ent->brushes;
197 b->oprev = ent->brushes.oprev;
198 ent->brushes.oprev->onext = b;
199 ent->brushes.oprev = b;
200 }
201 else
202 {
203 ep = ParseEpair ();
204 ep->next = ent->epairs;
205 ent->epairs = ep;
206 }
207 } while (1);
208
209 if (onlypairs)
210 return ent;
211
212 if (ent->brushes.onext == &ent->brushes)
213 has_brushes = false;
214 else
215 has_brushes = true;
216
217 GetVectorForKey (ent, "origin", ent->origin);
218
219 e = Eclass_ForName (ValueForKey (ent, "classname"), has_brushes);
220 ent->eclass = e;
221 if (e->fixedsize)
222 { // fixed size entity
223 if (ent->brushes.onext != &ent->brushes)
224 {
225 printf ("Warning: Fixed size entity with brushes\n");
226 #if 0
227 while (ent->brushes.onext != &ent->brushes)
228 { // FIXME: this will free the entity and crash!
229 Brush_Free (b);
230 }
231 #endif
232 ent->brushes.next = ent->brushes.prev = &ent->brushes;
233 }
234 // create a custom brush
235 VectorAdd (e->mins, ent->origin, mins);
236 VectorAdd (e->maxs, ent->origin, maxs);
237 b = Brush_Create (mins, maxs, &e->texdef);
238 b->owner = ent;
239
240 b->onext = ent->brushes.onext;
241 b->oprev = &ent->brushes;
242 ent->brushes.onext->oprev = b;
243 ent->brushes.onext = b;
244 }
245 else
246 { // brush entity
247 if (ent->brushes.next == &ent->brushes)
248 printf ("Warning: Brush entity with no brushes\n");
249 }
250
251 // add all the brushes to the main list
252 for (b=ent->brushes.onext ; b != &ent->brushes ; b=b->onext)
253 {
254 b->next = active_brushes.next;
255 active_brushes.next->prev = b;
256 b->prev = &active_brushes;
257 active_brushes.next = b;
258 }
259
260 return ent;
261 }
262
263 /*
264 ============
265 Entity_Write
266 ============
267 */
Entity_Write(entity_t * e,FILE * f,qboolean use_region)268 void Entity_Write (entity_t *e, FILE *f, qboolean use_region)
269 {
270 epair_t *ep;
271 brush_t *b;
272 vec3_t origin;
273 char text[128];
274 int count;
275
276 // if none of the entities brushes are in the region,
277 // don't write the entity at all
278 if (use_region)
279 {
280 // in region mode, save the camera position as playerstart
281 if ( !strcmp(ValueForKey (e, "classname"), "info_player_start") )
282 {
283 fprintf (f, "{\n");
284 fprintf (f, "\"classname\" \"info_player_start\"\n");
285 fprintf (f, "\"origin\" \"%i %i %i\"\n", (int)camera.origin[0],
286 (int)camera.origin[1], (int)camera.origin[2]);
287 fprintf (f, "\"angle\" \"%i\"\n", (int)camera.angles[YAW]);
288 fprintf (f, "}\n");
289 return;
290 }
291
292 for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
293 if (!Map_IsBrushFiltered(b))
294 break; // got one
295
296 if (b == &e->brushes)
297 return; // nothing visible
298 }
299
300 // if fixedsize, calculate a new origin based on the current
301 // brush position
302 if (e->eclass->fixedsize)
303 {
304 VectorSubtract (e->brushes.onext->mins, e->eclass->mins, origin);
305 sprintf (text, "%i %i %i", (int)origin[0],
306 (int)origin[1], (int)origin[2]);
307 SetKeyValue (e, "origin", text);
308 }
309
310 fprintf (f, "{\n");
311 for (ep = e->epairs ; ep ; ep=ep->next)
312 fprintf (f, "\"%s\" \"%s\"\n", ep->key, ep->value);
313
314 if (!e->eclass->fixedsize)
315 {
316 count = 0;
317 for (b=e->brushes.onext ; b != &e->brushes ; b=b->onext)
318 {
319 if (!use_region || !Map_IsBrushFiltered (b))
320 {
321 fprintf (f, "// brush %i\n", count);
322 count++;
323 Brush_Write (b, f);
324 }
325 }
326 }
327 fprintf (f, "}\n");
328 }
329
330
331
332 /*
333 ============
334 Entity_Create
335
336 Creates a new entity out of the selected_brushes list.
337 If the entity class is fixed size, the brushes are only
338 used to find a midpoint. Otherwise, the brushes have
339 their ownershi[ transfered to the new entity.
340 ============
341 */
Entity_Create(eclass_t * c)342 entity_t *Entity_Create (eclass_t *c)
343 {
344 entity_t *e;
345 brush_t *b;
346 vec3_t mins, maxs;
347 int i;
348
349 // check to make sure the brushes are ok
350
351 for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
352 if (b->owner != world_entity)
353 {
354 Sys_Printf ("Entity NOT created, brushes not all from world\n");
355 Sys_Beep ();
356 return NULL;
357 }
358
359 // create it
360
361 e = qmalloc(sizeof(*e));
362 e->brushes.onext = e->brushes.oprev = &e->brushes;
363 e->eclass = c;
364 SetKeyValue (e, "classname", c->name);
365
366 // add the entity to the entity list
367 e->next = entities.next;
368 entities.next = e;
369 e->next->prev = e;
370 e->prev = &entities;
371
372 if (c->fixedsize)
373 {
374 //
375 // just use the selection for positioning
376 //
377 b = selected_brushes.next;
378 for (i=0 ; i<3 ; i++)
379 e->origin[i] = b->mins[i] - c->mins[i];
380
381 // create a custom brush
382 VectorAdd (c->mins, e->origin, mins);
383 VectorAdd (c->maxs, e->origin, maxs);
384 b = Brush_Create (mins, maxs, &c->texdef);
385
386 Entity_LinkBrush (e, b);
387
388 // delete the current selection
389 Select_Delete ();
390
391 // select the new brush
392 b->next = b->prev = &selected_brushes;
393 selected_brushes.next = selected_brushes.prev = b;
394
395 Brush_Build( b );
396 }
397 else
398 {
399 //
400 // change the selected brushes over to the new entity
401 //
402 for (b=selected_brushes.next ; b != &selected_brushes ; b=b->next)
403 {
404 Entity_UnlinkBrush (b);
405 Entity_LinkBrush (e, b);
406 Brush_Build( b ); // so the key brush gets a name
407 }
408 }
409
410 Sys_UpdateWindows (W_ALL);
411 return e;
412 }
413
414
415 /*
416 ===========
417 Entity_LinkBrush
418 ===========
419 */
Entity_LinkBrush(entity_t * e,brush_t * b)420 void Entity_LinkBrush (entity_t *e, brush_t *b)
421 {
422 if (b->oprev || b->onext)
423 Error ("Entity_LinkBrush: Allready linked");
424 b->owner = e;
425
426 b->onext = e->brushes.onext;
427 b->oprev = &e->brushes;
428 e->brushes.onext->oprev = b;
429 e->brushes.onext = b;
430 }
431
432 /*
433 ===========
434 Entity_UnlinkBrush
435 ===========
436 */
Entity_UnlinkBrush(brush_t * b)437 void Entity_UnlinkBrush (brush_t *b)
438 {
439 if (!b->owner || !b->onext || !b->oprev)
440 Error ("Entity_UnlinkBrush: Not currently linked");
441 b->onext->oprev = b->oprev;
442 b->oprev->onext = b->onext;
443 b->onext = b->oprev = NULL;
444 b->owner = NULL;
445 }
446
447
448
449 /*
450 ===========
451 Entity_Clone
452 ===========
453 */
Entity_Clone(entity_t * e)454 entity_t *Entity_Clone (entity_t *e)
455 {
456 entity_t *n;
457 epair_t *ep, *np;
458
459 n = qmalloc(sizeof(*n));
460 n->brushes.onext = n->brushes.oprev = &n->brushes;
461 n->eclass = e->eclass;
462
463 // add the entity to the entity list
464 n->next = entities.next;
465 entities.next = n;
466 n->next->prev = n;
467 n->prev = &entities;
468
469 for (ep = e->epairs ; ep ; ep=ep->next)
470 {
471 np = qmalloc(sizeof(*np));
472 np->key = copystring(ep->key);
473 np->value = copystring(ep->value);
474 np->next = n->epairs;
475 n->epairs = np;
476 }
477 return n;
478 }
479
GetUniqueTargetId(int iHint)480 int GetUniqueTargetId(int iHint)
481 {
482 int iMin, iMax, i;
483 BOOL fFound;
484 entity_t *pe;
485
486 fFound = FALSE;
487 pe = entities.next;
488 iMin = 0;
489 iMax = 0;
490
491 for (; pe != NULL && pe != &entities ; pe = pe->next)
492 {
493 i = IntForKey(pe, "target");
494 if (i)
495 {
496 iMin = min(i, iMin);
497 iMax = max(i, iMax);
498 if (i == iHint)
499 fFound = TRUE;
500 }
501 }
502
503 if (fFound)
504 return iMax + 1;
505 else
506 return iHint;
507 }
508
FindEntity(char * pszKey,char * pszValue)509 entity_t *FindEntity(char *pszKey, char *pszValue)
510 {
511 entity_t *pe;
512
513 pe = entities.next;
514
515 for (; pe != NULL && pe != &entities ; pe = pe->next)
516 {
517 if (!strcmp(ValueForKey(pe, pszKey), pszValue))
518 return pe;
519 }
520
521 return NULL;
522 }
523
FindEntityInt(char * pszKey,int iValue)524 entity_t *FindEntityInt(char *pszKey, int iValue)
525 {
526 entity_t *pe;
527
528 pe = entities.next;
529
530 for (; pe != NULL && pe != &entities ; pe = pe->next)
531 {
532 if (IntForKey(pe, pszKey) == iValue)
533 return pe;
534 }
535
536 return NULL;
537 }
538
539