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 }