ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/object.C
(Generate patch)

Comparing deliantra/server/common/object.C (file contents):
Revision 1.11 by root, Sun Sep 3 00:18:40 2006 UTC vs.
Revision 1.15 by root, Mon Sep 4 13:55:54 2006 UTC

1/* 1/*
2 * static char *rcsid_object_c = 2 * static char *rcsid_object_c =
3 * "$Id: object.C,v 1.11 2006/09/03 00:18:40 root Exp $"; 3 * "$Id: object.C,v 1.15 2006/09/04 13:55:54 root Exp $";
4 */ 4 */
5 5
6/* 6/*
7 CrossFire, A Multiplayer game for X-windows 7 CrossFire, A Multiplayer game for X-windows
8 8
37#endif /* win32 */ 37#endif /* win32 */
38#include <object.h> 38#include <object.h>
39#include <funcpoint.h> 39#include <funcpoint.h>
40#include <skills.h> 40#include <skills.h>
41#include <loader.h> 41#include <loader.h>
42#ifdef MEMORY_DEBUG
43int nroffreeobjects = 0;
44int nrofallocobjects = 0; 42int nrofallocobjects = 0;
45#undef OBJ_EXPAND
46#define OBJ_EXPAND 1
47#else
48object objarray[STARTMAX]; /* All objects, allocated this way at first */
49int nroffreeobjects = STARTMAX; /* How many OBs allocated and free (free) */
50int nrofallocobjects = STARTMAX; /* How many OBs allocated (free + used) */
51#endif
52 43
53object *objects; /* Pointer to the list of used objects */ 44object *objects; /* Pointer to the list of used objects */
54object *free_objects; /* Pointer to the list of unused objects */
55object *active_objects; /* List of active objects that need to be processed */ 45object *active_objects; /* List of active objects that need to be processed */
56 46
57short freearr_x[SIZEOFFREE]= 47short freearr_x[SIZEOFFREE]=
58 {0,0,1,1,1,0,-1,-1,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,-2,-2,-2,-1, 48 {0,0,1,1,1,0,-1,-1,-1,0,1,2,2,2,2,2,1,0,-1,-2,-2,-2,-2,-2,-1,
59 0,1,2,3,3,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-3,-3,-3,-3,-3,-2,-1}; 49 0,1,2,3,3,3,3,3,3,3,2,1,0,-1,-2,-3,-3,-3,-3,-3,-3,-3,-2,-1};
388 break; 378 break;
389 379
390 return op; 380 return op;
391} 381}
392 382
393void free_all_object_data(void) { 383void free_all_object_data ()
394#ifdef MEMORY_DEBUG 384{
395 object *op, *next; 385 LOG (llevDebug, "%d allocated objects\n", nrofallocobjects);
396
397 for (op=free_objects; op!=NULL; ) {
398 next=op->next;
399 free(op);
400 nrofallocobjects--;
401 nroffreeobjects--;
402 op=next;
403 }
404#endif
405 LOG(llevDebug,"%d allocated objects, %d free objects, STARMAX=%d\n",
406 nrofallocobjects, nroffreeobjects,STARTMAX);
407} 386}
408 387
409/* 388/*
410 * Returns the object which this object marks as being the owner. 389 * Returns the object which this object marks as being the owner.
411 * A id-scheme is used to avoid pointing to objects which have been 390 * A id-scheme is used to avoid pointing to objects which have been
438 op->owner->refcount--; 417 op->owner->refcount--;
439 418
440 op->owner = NULL; 419 op->owner = NULL;
441 op->ownercount = 0; 420 op->ownercount = 0;
442} 421}
443
444
445 422
446/* 423/*
447 * Sets the owner and sets the skill and exp pointers to owner's current 424 * Sets the owner and sets the skill and exp pointers to owner's current
448 * skill and experience objects. 425 * skill and experience objects.
449 */ 426 */
496 } 473 }
497 set_owner(op, owner); 474 set_owner(op, owner);
498 475
499} 476}
500 477
501/*
502 * Resets vital variables in an object
503 */
504
505void reset_object(object *op) {
506
507 op->name = NULL;
508 op->name_pl = NULL;
509 op->title = NULL;
510 op->race = NULL;
511 op->slaying = NULL;
512 op->skill = NULL;
513 op->msg = NULL;
514 op->materialname = NULL;
515 op->lore = NULL;
516 clear_object(op);
517}
518
519/* Zero the key_values on op, decrementing the shared-string 478/* Zero the key_values on op, decrementing the shared-string
520 * refcounts and freeing the links. 479 * refcounts and freeing the links.
521 */ 480 */
522static void free_key_values(object * op) 481static void free_key_values(object * op)
523{ 482{
529 } 488 }
530 489
531 op->key_values = 0; 490 op->key_values = 0;
532} 491}
533 492
493void object::clear ()
494{
495 attachable_base::clear ();
496
497 free_key_values (this);
498
499 name = 0;
500 name_pl = 0;
501 title = 0;
502 race = 0;
503 slaying = 0;
504 skill = 0;
505 msg = 0;
506 lore = 0;
507 custom_name = 0;
508 materialname = 0;
509
510 memset (static_cast<object_pod *>(this), 0, sizeof (object_pod));
511
512 SET_FLAG (this, FLAG_REMOVED);
513}
514
515void object::clone (object *destination)
516{
517 *(object_copy *)destination = *(object_copy *)this;
518 *(object_pod *)destination = *(object_pod *)this;
519
520 if (self || cb)
521 INVOKE_OBJECT (CLONE, this, ARG_OBJECT (destination));
522}
523
534/* 524/*
535 * clear_object() frees everything allocated by an object, and also 525 * clear_object() frees everything allocated by an object, and also
536 * clears all variables and flags to default settings. 526 * clears all variables and flags to default settings.
537 */ 527 */
538 528
539void clear_object(object *op) 529void clear_object (object *op)
540{ 530{
541 op->clear (); 531 op->clear ();
542
543 free_key_values (op);
544
545 /* the memset will clear all these values for us, but we need
546 * to reduce the refcount on them.
547 */
548 op->name = 0;
549 op->name_pl = 0;
550 op->title = 0;
551 op->race = 0;
552 op->slaying = 0;
553 op->skill = 0;
554 op->msg = 0;
555 op->lore = 0;
556 op->materialname = 0;
557
558 memset((void*)op, 0, sizeof (object_special));
559 /* Below here, we clear things that are not done by the memset,
560 * or set default values that are not zero.
561 */
562 /* This is more or less true */
563 SET_FLAG (op, FLAG_REMOVED);
564 532
565 op->contr = NULL; 533 op->contr = NULL;
566 op->below = NULL; 534 op->below = NULL;
567 op->above = NULL; 535 op->above = NULL;
568 op->inv = NULL; 536 op->inv = NULL;
574 op->refcount=0; 542 op->refcount=0;
575 op->active_next = NULL; 543 op->active_next = NULL;
576 op->active_prev = NULL; 544 op->active_prev = NULL;
577 /* What is not cleared is next, prev, and count */ 545 /* What is not cleared is next, prev, and count */
578 546
579 op->expmul=1.0; 547 op->expmul = 1.0;
580 op->face = blank_face; 548 op->face = blank_face;
581 op->attacked_by_count = (tag_t) -1; 549 op->attacked_by_count = -1;
582 550
583 if (settings.casting_time) 551 if (settings.casting_time)
584 op->casting_time = -1; 552 op->casting_time = -1;
585} 553}
586 554
591 * data that is malloc'd needs to be re-malloc/copied. Otherwise, 559 * data that is malloc'd needs to be re-malloc/copied. Otherwise,
592 * if the first object is freed, the pointers in the new object 560 * if the first object is freed, the pointers in the new object
593 * will point at garbage. 561 * will point at garbage.
594 */ 562 */
595 563
596void copy_object(object *op2, object *op) 564void copy_object (object *op2, object *op)
597{ 565{
598 int is_freed=QUERY_FLAG(op,FLAG_FREED),is_removed=QUERY_FLAG(op,FLAG_REMOVED); 566 bool is_freed = QUERY_FLAG (op, FLAG_FREED);
567 bool is_removed = QUERY_FLAG (op, FLAG_REMOVED);
599 568
600 op->clear ();
601
602 free_key_values (op);
603
604 *(object_special *)op = *(object_special *)op2;
605 op2->clone (op); 569 op2->clone (op);
606 570
607 if (is_freed) SET_FLAG(op,FLAG_FREED); 571 if (is_freed) SET_FLAG (op, FLAG_FREED);
608 if (is_removed) SET_FLAG(op,FLAG_REMOVED); 572 if (is_removed) SET_FLAG (op, FLAG_REMOVED);
609 573
610 if (op2->speed < 0) 574 if (op2->speed < 0)
611 op->speed_left = op2->speed_left - RANDOM() % 200 / 100.0; 575 op->speed_left = op2->speed_left - RANDOM() % 200 / 100.0;
612 576
613 /* Copy over key_values, if any. */ 577 /* Copy over key_values, if any. */
614 if (op2->key_values != NULL) { 578 if (op2->key_values != NULL)
579 {
615 key_value *tail = NULL; 580 key_value *tail = NULL;
616 key_value *i; 581 key_value *i;
617 582
618 op->key_values = NULL; 583 op->key_values = NULL;
619 584
634 else 599 else
635 { 600 {
636 tail->next = new_link; 601 tail->next = new_link;
637 tail = new_link; 602 tail = new_link;
638 } 603 }
639 } 604 }
640 } 605 }
641 606
642 update_ob_speed (op); 607 update_ob_speed (op);
643}
644
645/*
646 * expand_objects() allocates more objects for the list of unused objects.
647 * It is called from get_object() if the unused list is empty.
648 */
649
650void expand_objects(void) {
651 int i;
652 object *obj;
653 obj = new object [OBJ_EXPAND];
654
655 free_objects=obj;
656 obj[0].prev=NULL;
657 obj[0].next= &obj[1],
658 SET_FLAG(&(obj[0]), FLAG_REMOVED);
659 SET_FLAG(&(obj[0]), FLAG_FREED);
660
661 for(i=1;i<OBJ_EXPAND-1;i++) {
662 obj[i].next= &obj[i+1],
663 obj[i].prev= &obj[i-1],
664 SET_FLAG(&(obj[i]), FLAG_REMOVED);
665 SET_FLAG(&(obj[i]), FLAG_FREED);
666 }
667 obj[OBJ_EXPAND-1].prev= &obj[OBJ_EXPAND-2],
668 obj[OBJ_EXPAND-1].next=NULL,
669 SET_FLAG(&(obj[OBJ_EXPAND-1]), FLAG_REMOVED);
670 SET_FLAG(&(obj[OBJ_EXPAND-1]), FLAG_FREED);
671
672 nrofallocobjects += OBJ_EXPAND;
673 nroffreeobjects += OBJ_EXPAND;
674} 608}
675 609
676/* 610/*
677 * get_object() grabs an object from the list of unused objects, makes 611 * get_object() grabs an object from the list of unused objects, makes
678 * sure it is initialised, and returns it. 612 * sure it is initialised, and returns it.
679 * If there are no free objects, expand_objects() is called to get more. 613 * If there are no free objects, expand_objects() is called to get more.
680 */ 614 */
681 615
682object *get_object(void) 616object *get_object ()
683{ 617{
684 object *op; 618 object *op = new object;
685 619
686 if(free_objects==NULL)
687 expand_objects();
688
689 op = free_objects;
690
691 if (!QUERY_FLAG (op, FLAG_FREED))
692 LOG(llevError,"Fatal: Getting busy object.\n");
693
694 free_objects = op->next;
695
696 if (free_objects != NULL)
697 free_objects->prev = NULL;
698
699 op->count= ++ob_count; 620 op->count = ++ob_count;
700 op->name=NULL; 621
701 op->name_pl=NULL; 622 op->active_next = 0;
702 op->title=NULL; 623 op->active_prev = 0;
703 op->race=NULL; 624
704 op->slaying=NULL;
705 op->skill = NULL;
706 op->lore=NULL;
707 op->msg=NULL;
708 op->materialname=NULL;
709 op->next=objects; 625 op->next = objects;
710 op->prev=NULL; 626 op->prev = 0;
711 op->active_next = NULL; 627
712 op->active_prev = NULL;
713 if(objects!=NULL) 628 if (objects)
714 objects->prev=op; 629 objects->prev = op;
630
715 objects=op; 631 objects = op;
716 clear_object(op); 632
717 SET_FLAG(op,FLAG_REMOVED); 633 SET_FLAG (op, FLAG_REMOVED);
718 nroffreeobjects--; 634
635 op->expmul = 1.0;
636 op->face = blank_face;
637 op->attacked_by_count = -1;
638
719 return op; 639 return op;
720} 640}
721 641
722/* 642/*
723 * If an object with the IS_TURNABLE() flag needs to be turned due 643 * If an object with the IS_TURNABLE() flag needs to be turned due
931 * 851 *
932 * If free_inventory is set, free inventory as well. Else drop items in 852 * If free_inventory is set, free inventory as well. Else drop items in
933 * inventory to the ground. 853 * inventory to the ground.
934 */ 854 */
935 855
856void
936void free_object(object *ob) { 857free_object (object * ob)
858{
937 free_object2(ob, 0); 859 free_object2 (ob, 0);
938} 860}
861
862void
939void free_object2(object *ob, int free_inventory) { 863free_object2 (object * ob, int free_inventory)
864{
940 object *tmp,*op; 865 object *tmp, *op;
941 866
942 ob->clear ();
943
944 if (!QUERY_FLAG(ob,FLAG_REMOVED)) { 867 if (!QUERY_FLAG (ob, FLAG_REMOVED))
868 {
945 LOG(llevDebug,"Free object called with non removed object\n"); 869 LOG (llevDebug, "Free object called with non removed object\n");
946 dump_object(ob); 870 dump_object (ob);
947#ifdef MANY_CORES 871#ifdef MANY_CORES
948 abort(); 872 abort ();
949#endif 873#endif
950 } 874 }
875
951 if(QUERY_FLAG(ob,FLAG_FRIENDLY)) { 876 if (QUERY_FLAG (ob, FLAG_FRIENDLY))
877 {
952 LOG(llevMonster,"Warning: tried to free friendly object.\n"); 878 LOG (llevMonster, "Warning: tried to free friendly object.\n");
953 remove_friendly_object(ob); 879 remove_friendly_object (ob);
954 } 880 }
881
955 if(QUERY_FLAG(ob,FLAG_FREED)) { 882 if (QUERY_FLAG (ob, FLAG_FREED))
883 {
956 dump_object(ob); 884 dump_object (ob);
957 LOG(llevError,"Trying to free freed object.\n%s\n",errmsg); 885 LOG (llevError, "Trying to free freed object.\n%s\n", errmsg);
958 return; 886 return;
959 } 887 }
888
960 if(ob->more!=NULL) { 889 if (ob->more != NULL)
890 {
961 free_object2(ob->more, free_inventory); 891 free_object2 (ob->more, free_inventory);
962 ob->more=NULL; 892 ob->more = NULL;
963 } 893 }
894
964 if (ob->inv) { 895 if (ob->inv)
896 {
965 /* Only if the space blocks everything do we not process - 897 /* Only if the space blocks everything do we not process -
966 * if some form of movemnt is allowed, let objects 898 * if some form of movemnt is allowed, let objects
967 * drop on that space. 899 * drop on that space.
968 */ 900 */
969 if (free_inventory || ob->map==NULL || ob->map->in_memory!=MAP_IN_MEMORY || 901 if (free_inventory || ob->map == NULL
902 || ob->map->in_memory != MAP_IN_MEMORY
970 (GET_MAP_MOVE_BLOCK(ob->map, ob->x, ob->y) == MOVE_ALL)) 903 || (GET_MAP_MOVE_BLOCK (ob->map, ob->x, ob->y) == MOVE_ALL))
971 { 904 {
972 op=ob->inv; 905 op = ob->inv;
973 while(op!=NULL) { 906
907 while (op != NULL)
908 {
974 tmp=op->below; 909 tmp = op->below;
975 remove_ob(op); 910 remove_ob (op);
976 free_object2(op, free_inventory); 911 free_object2 (op, free_inventory);
977 op=tmp; 912 op = tmp;
978 }
979 }
980 else { /* Put objects in inventory onto this space */
981 op=ob->inv;
982 while(op!=NULL) {
983 tmp=op->below;
984 remove_ob(op);
985 if(QUERY_FLAG(op,FLAG_STARTEQUIP)||QUERY_FLAG(op,FLAG_NO_DROP) ||
986 op->type==RUNE || op->type==TRAP || QUERY_FLAG(op,FLAG_IS_A_TEMPLATE))
987 free_object(op);
988 else {
989 op->x=ob->x;
990 op->y=ob->y;
991 insert_ob_in_map(op,ob->map,NULL,0); /* Insert in same map as the envir */
992 }
993 op=tmp;
994 }
995 }
996 } 913 }
914 }
915 else
916 { /* Put objects in inventory onto this space */
917 op = ob->inv;
918
919 while (op != NULL)
920 {
921 tmp = op->below;
922 remove_ob (op);
923
924 if (QUERY_FLAG (op, FLAG_STARTEQUIP)
925 || QUERY_FLAG (op, FLAG_NO_DROP) || op->type == RUNE
926 || op->type == TRAP || QUERY_FLAG (op, FLAG_IS_A_TEMPLATE))
927 free_object (op);
928 else
929 {
930 op->x = ob->x;
931 op->y = ob->y;
932 insert_ob_in_map (op, ob->map, NULL, 0); /* Insert in same map as the envir */
933 }
934
935 op = tmp;
936 }
937 }
938 }
939
997 /* Remove object from the active list */ 940 /* Remove object from the active list */
998 ob->speed = 0; 941 ob->speed = 0;
999 update_ob_speed(ob); 942 update_ob_speed (ob);
1000 943
1001 SET_FLAG(ob, FLAG_FREED); 944 SET_FLAG (ob, FLAG_FREED);
1002 ob->count = 0; 945 ob->count = 0;
1003 946
1004 /* Remove this object from the list of used objects */ 947 /* Remove this object from the list of used objects */
1005 if(ob->prev==NULL) { 948 if (ob->prev == NULL)
949 {
1006 objects=ob->next; 950 objects = ob->next;
951
1007 if(objects!=NULL) 952 if (objects != NULL)
1008 objects->prev=NULL; 953 objects->prev = NULL;
954 }
955 else
1009 } 956 {
1010 else {
1011 ob->prev->next=ob->next; 957 ob->prev->next = ob->next;
958
1012 if(ob->next!=NULL) 959 if (ob->next != NULL)
1013 ob->next->prev=ob->prev; 960 ob->next->prev = ob->prev;
1014 } 961 }
1015
1016 ob->name = 0;
1017 ob->name_pl = 0;
1018 ob->title = 0;
1019 ob->race = 0;
1020 ob->slaying = 0;
1021 ob->skill = 0;
1022 ob->lore = 0;
1023 ob->msg = 0;
1024 ob->materialname = 0;
1025 962
1026 free_key_values (ob); 963 free_key_values (ob);
1027 964
1028 /* Now link it with the free_objects list: */ 965 /* Now link it with the free_objects list: */
1029 ob->prev=NULL; 966 ob->prev = 0;
1030 ob->next=free_objects; 967 ob->next = 0;
1031 968
1032 if(free_objects!=NULL) 969 delete ob;
1033 free_objects->prev=ob;
1034
1035 free_objects=ob;
1036 nroffreeobjects++;
1037}
1038
1039/*
1040 * count_free() returns the number of objects on the list of free objects.
1041 */
1042
1043int count_free ()
1044{
1045 int i=0;
1046 object *tmp=free_objects;
1047 while(tmp!=NULL)
1048 tmp=tmp->next, i++;
1049 return i;
1050}
1051
1052/*
1053 * count_used() returns the number of objects on the list of used objects.
1054 */
1055
1056int count_used()
1057{
1058 int i=0;
1059 object *tmp=objects;
1060 while(tmp!=NULL)
1061 tmp=tmp->next, i++;
1062 return i;
1063}
1064
1065/*
1066 * count_active() returns the number of objects on the list of active objects.
1067 */
1068
1069int count_active()
1070{
1071 int i=0;
1072 object *tmp=active_objects;
1073 while(tmp!=NULL)
1074 tmp=tmp->active_next, i++;
1075 return i;
1076} 970}
1077 971
1078/* 972/*
1079 * sub_weight() recursively (outwards) subtracts a number from the 973 * sub_weight() recursively (outwards) subtracts a number from the
1080 * weight of an object (and what is carried by it's environment(s)). 974 * weight of an object (and what is carried by it's environment(s)).
1158 /* we set up values so that it could be inserted into 1052 /* we set up values so that it could be inserted into
1159 * the map, but we don't actually do that - it is up 1053 * the map, but we don't actually do that - it is up
1160 * to the caller to decide what we want to do. 1054 * to the caller to decide what we want to do.
1161 */ 1055 */
1162 op->x=op->env->x,op->y=op->env->y; 1056 op->x=op->env->x,op->y=op->env->y;
1163 op->ox=op->x,op->oy=op->y;
1164 op->map=op->env->map; 1057 op->map=op->env->map;
1165 op->above=NULL,op->below=NULL; 1058 op->above=NULL,op->below=NULL;
1166 op->env=NULL; 1059 op->env=NULL;
1167 return; 1060 return;
1168 } 1061 }
1379 * we set the map, that doesn't work if the location is within 1272 * we set the map, that doesn't work if the location is within
1380 * a map and this is straddling an edge. So only if coordinate 1273 * a map and this is straddling an edge. So only if coordinate
1381 * is clear wrong do we normalize it. 1274 * is clear wrong do we normalize it.
1382 */ 1275 */
1383 if (OUT_OF_REAL_MAP(more->map, more->x, more->y)) { 1276 if (OUT_OF_REAL_MAP(more->map, more->x, more->y)) {
1384 /* Debugging information so you can see the last coordinates this object had */
1385 more->ox = more->x;
1386 more->oy = more->y;
1387 more->map = get_map_from_coord(m, &more->x, &more->y); 1277 more->map = get_map_from_coord(m, &more->x, &more->y);
1388 } else if (!more->map) { 1278 } else if (!more->map) {
1389 /* For backwards compatibility - when not dealing with tiled maps, 1279 /* For backwards compatibility - when not dealing with tiled maps,
1390 * more->map should always point to the parent. 1280 * more->map should always point to the parent.
1391 */ 1281 */
1397 LOG (llevError, "BUG: insert_ob_in_map(): inserting op->more killed op\n"); 1287 LOG (llevError, "BUG: insert_ob_in_map(): inserting op->more killed op\n");
1398 return NULL; 1288 return NULL;
1399 } 1289 }
1400 } 1290 }
1401 CLEAR_FLAG(op,FLAG_REMOVED); 1291 CLEAR_FLAG(op,FLAG_REMOVED);
1402
1403 /* Debugging information so you can see the last coordinates this object had */
1404 op->ox=op->x;
1405 op->oy=op->y;
1406 1292
1407 /* Ideally, the caller figures this out. However, it complicates a lot 1293 /* Ideally, the caller figures this out. However, it complicates a lot
1408 * of areas of callers (eg, anything that uses find_free_spot would now 1294 * of areas of callers (eg, anything that uses find_free_spot would now
1409 * need extra work 1295 * need extra work
1410 */ 1296 */
1812 op->map=NULL; 1698 op->map=NULL;
1813 op->env=where; 1699 op->env=where;
1814 op->above=NULL; 1700 op->above=NULL;
1815 op->below=NULL; 1701 op->below=NULL;
1816 op->x=0,op->y=0; 1702 op->x=0,op->y=0;
1817 op->ox=0,op->oy=0;
1818 1703
1819 /* reset the light list and los of the players on the map */ 1704 /* reset the light list and los of the players on the map */
1820 if((op->glow_radius!=0)&&where->map) 1705 if((op->glow_radius!=0)&&where->map)
1821 { 1706 {
1822#ifdef DEBUG_LIGHTS 1707#ifdef DEBUG_LIGHTS
2494 fclose(tempfile); 2379 fclose(tempfile);
2495 2380
2496 op=get_object(); 2381 op=get_object();
2497 2382
2498 object_thawer thawer (filename); 2383 object_thawer thawer (filename);
2384
2499 if (thawer) 2385 if (thawer)
2500 load_object(thawer,op,LO_NEWFILE,0); 2386 load_object(thawer,op,0);
2387
2501 LOG(llevDebug," load str completed, object=%s\n", &op->name); 2388 LOG(llevDebug," load str completed, object=%s\n", &op->name);
2502 CLEAR_FLAG(op,FLAG_REMOVED); 2389 CLEAR_FLAG(op,FLAG_REMOVED);
2503 2390
2504 return op; 2391 return op;
2505} 2392}

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines